the console + marketing site. proxies every backend call so the service token never reaches the browser.
architecture
Four services, one loop.
The console is the thin face of a small fleet of Rust services. Each agent's life is a loop across them: bind a key, refill it from the Sui account, use it on OpenRouter, repeat.
Services
spawns Sui agent objects and signs locally built Sui transactions. deposits treasury USDC into agent accounts, handles bounded refill requests, and runs the harness self-care loop that drives each agent tick.
the ledger. mints keys, indexes Sui Charged events into API credit, records off-chain call debits, and serves the activity feed.
OpenRouter forwarder. reachable at its public domain. it accepts either the service token plus X-Daimon-Key (internal callers) or an owner dk_ credential released through an OwnerCap challenge. a public key ID alone cannot authorize a call. usage is debited off-chain.
Request paths
browser ──► daimon-web ──► (service token)
│ │
│ ├──► daimon-supervisor spawn agent · deposit USDC · treasury status
│ └──► daimon-issuer fleet by-owner · agent activity · pause/resume
│
│ the agent loop (harness inside the supervisor, no browser):
│
daimon-supervisor ──► daimon-issuer mint key · index charges · debit
├──► daimon-x402-proxy ──► OpenRouter use key (per-call debit)
├──► Sui mainnet account::charge
└──► Sui mainnet account::bind_keyThe console only ever talks to daimon-web, which holds the service token and fans out to the supervisor and issuer. The agent's actual work happens in the harness, off the request path — the console just polls the issuer ledger to show what the agent did.
Trust boundary
- service token — shared secret between web and the Rust services. Never sent to the browser; every privileged call is proxied through a web API route.
- owner token— minted at spawn, stored in the browser's localStorage. Unlocks pause/resume for the agents you spawned. Never re-exposed by the issuer.
- agent account— USDC lives inside the agent's Sui object. Anyone can deposit. Only its OwnerCap holder can withdraw; for Telegram agents, that holder is the operator. IssuerCap charges go only to the configured treasury, within per-charge and daily caps ($1 per charge, $5 per day), and stop when paused. Deposits are capped at 50 USDC per account.
State on chain
The agent's identity is a Move shared object minted by daimon::agent::new. Its USDC balance is a dynamic field managed by daimon::account. The active public key ID is bound by account::bind_key; private credentials stay off-chain. Refills emit Charged, deposits emit Deposited, and withdrawals emit Withdrawn. Each has a Sui transaction receipt. Model calls and walks are not recorded on chain; individual call debits stay in the issuer ledger.
Both objects below are on Sui mainnet. Check them on suiscan.