ORRANGE / Architecture
Security Model
Short and serious: the boundaries that make the copilot safe to use on real money.
What ORRANGE never handles
The dapp never receives or stores:
- Viewing keys — they live in the connected privacy wallet.
- Encrypted notes or decrypted notes.
- Private keys — the browser never holds them (Privy signs server-side; Ready signs in-wallet).
- Nullifiers and proofs.
Private balances come only from the wallet (wallet_strk20Balances / SDK discovery), never from a local cache.
What the AI receives
Hamster receives only the aggregate portfolio summary necessary for analysis: balances, USD values, allocations, and liquidity — plus the active policy. It never receives notes, viewing keys, or per-transaction metadata.
What the AI cannot do
- Sign — only your wallet signs.
- Choose arbitrary calldata — the only action is a STRK20 private transfer to an approved destination.
- Weaken policy — user-selected, server-validated; model constraints are rejected.
- Bypass destination controls — an explicit allowlist gates every recipient.
Threats and mitigations
| Threat | Mitigation |
|---|---|
| Stale state | Execution re-fetches current balances and aborts if they differ from analysis-time state (STATE_CHANGED). |
| Prompt injection | Model output is schema-validated, cannot add constraints or destinations, and is advisory-only. The policy engine never trusts the model. |
| Malicious destination | Recipient must be in the approved allowlist; the treasury identity itself is rejected as a self-transfer. |
| Stale price | Volatile assets require a fresh live AVNU price (< 60s) to authorize execution. |
| Client-modified balances | Balances are analysis input, but execution re-checks against the wallet and re-runs the policy; prices are resolved fresh on the server. |
| Expired proposals | A 120 s TTL invalidates analysis; execution refuses an expired proposal. |
| Arbitrary calldata | No generic invoke exists from the copilot — only the existing private-transfer path. |
Signing boundary
- Ready lane: the wallet signs after you approve in its UI; the app only requests authorized Wallet API operations.
- Privy lane:
/api/privy/signrequires an authenticated Privy JWT and signs only the pre-computed transaction hash — no blind or arbitrary transactions.
The full integration audit lives in docs/PRIVY_STRK20_AUDIT.md. If you find a way to weaken these boundaries, please report it responsibly via GitHub.
