AI + Policy Architecture
Hamster proposes. The deterministic policy engine decides. This separation is the entire security story.
user prompt
│
▼
AI proposal (structured JSON)
│
▼
schema validation (no constraints accepted)
│
▼
portfolio + fresh prices (server-side)
│
▼
deterministic TreasuryPolicy verdict
│
▼
ADVISORY / BLOCKED / EXECUTABLE
│
▼
user confirmation
│
▼
fresh state re-fetch + policy re-evaluation
│
▼
existing STRK20 private transferThe separation
The LLM is advisory and untrusted. Its output is a JSON proposal that is schema-validated and then evaluated by a pure, deterministic policy engine. The engine has no network access and no LLM — it is a function of (proposal, portfolio, policy). The model can never change the policy, choose a destination outside the allowlist, or emit arbitrary calldata.
What the AI can control
- Whether to propose an action or a report.
- Which treasury asset to move, and a human-readable amount string.
- Concise display copy (diagnosis, recommendation, why, outcome) — never used for decisions.
What the AI can never control
- Policy values — user-selected, server-validated; model-injected
constraintsare rejected. - Destinations — an explicit allowlist; an empty allowlist denies everything.
- Execution — only the existing STRK20 private-transfer path, only after re-validation.
Deterministic checks
| Check | Rule |
|---|---|
| Exact amount | Amounts are parsed to exact bigint base units via the asset’s decimals (parseAmountExact); over-precision is rejected, never rounded. |
| Balance | Proposed base units must not exceed the position’s balance. |
| Destination allowlist | Recipient must be in the approved set. Empty set = deny all execution. |
| Self-transfer rejection | A recipient equal to the treasury identity is rejected deterministically. |
| Live price for volatile assets | STRK/ETH need a fresh (< 60s) AVNU price to authorize execution; a static fallback only feeds advisory analysis. |
| Stablecoins | USDC/USDT are pinned at $1 (static is authoritative). |
| Min liquidity | USD liquidity after the action must stay ≥ your guardrail floor (conservative bigint cents). |
| Max position | No single position may exceed your cap after the action (integer bps, no float division). |
| Max per action | The action’s conservative USD value must stay ≤ your cap. |
User-selected policy
The guardrail is chosen by the user (preset or custom) and sent with every analysis request. The server validates bounds (resolveUserPolicy) and returns the effective policy with the response. Values:
- Min liquid:
0 – $1,000,000 - Position cap:
1 – 100% - Max per action:
$1 – $10,000,000
Out-of-bounds or unknown presets are rejected with 400 — never silently clamped.
Execution gate
Confirming runs executeProposal, which re-checks in order: expiry (120 s TTL) → current balances equal analysis-time state → exact bigint amount reconstruction → fresh prices → deterministic policy against fresh state → the injected STRK20 private-transfer path. Any failure returns a specific reason (EXPIRED, STATE_CHANGED, AMOUNT_INVALID, POLICY_REJECTED, EXECUTION_FAILED) and no transfer occurs.
