Trust model
What each participant can and cannot do, and which properties survive a compromise of each component.
Titan is non-custodial in a specific, checkable sense: no component between the wallet and the engine can produce a valid order signature.
The three keys
Prop
Type
Compromise analysis
| Compromised | Attacker gains | Attacker cannot |
|---|---|---|
| BFF | Read every user's projected history; observe the firehose | Place, cancel, or modify any order — no signature capability |
| Session key | Trade as that user until expiresAt | Withdraw (withdrawals are on-chain, wallet-signed) |
| Indexer | Corrupt the projection | Change custody or engine state — Postgres is downstream of both |
| Engine | Mis-match, mis-report positions | Move collateral without an on-chain withdrawal approval |
| Wallet key | Everything for that party | Affect any other party |
A session key is a trading credential, not a custody credential. Its blast radius is bounded
by expiresAt and by the fact that withdrawal requests are contracts on the ledger, signed by
the wallet key.
Why the BFF forwards headers verbatim
The obvious alternative — the BFF re-signs on behalf of the user — would collapse the whole model. It would need the session private key, which would make a BFF breach equivalent to a wallet breach for every active trader.
So the contract is deliberately awkward: the browser produces the canonical bytes, signs them, and sends three headers the BFF is required to copy without inspection.
POST /v1/orders
X-User: Alice::1220abcd…
X-Session-Id: sess_7f2c…
X-Session-Sig: 3a91f0… # hex(ed25519 sig over canonical bytes)
Content-Type: application/json
{"market":"BTC-USDCX","side":"buy","size":"0.05","price":"64000","nonce":42}If the body and the signed bytes disagree by a single byte, the engine rejects. This is the property that makes the pass-through safe.
Auditability
Every fill lands in a per-user merkle tree; every batch of trees produces a manifest committed on-chain. That gives two independent verification paths, both exposed by the BFF:
| Route | Checks |
|---|---|
GET /v1/verifications/user-batches | Engine's per-user leaves ↔ on-chain UserBatch |
GET /v1/verifications/manifests | Engine's batch manifest ↔ on-chain BatchManifest |
A mismatch there is the canary for engine misbehaviour — it does not require trusting the engine's own reporting.