Skip to content

PreviewPayments here are simulated. Don't send real crypto to any address on this site.

Sample accounts
Sealed

Docs

Threat model

What can go wrong, what stops it, and what is left. Written for the security audit and for engineers changing the system; docs/TRUST_MODEL.md says the same in plain English for users. References point to code and to the repository's DECISIONS.md.

What we protect

Asset Where it lives
Handles' funds (system key) Each handle's NEAR Intents account; the vault is the only account the MPC network signs its key for
Handles' funds (self-custodied) Same account, controlled only by the owner's key after R7
Public deposits in transit The handle's derived EVM, Bitcoin and Solana addresses, until swept
Privacy Who received what: confidential balances, private-send recipients, the link between an X account and its activity
Sessions 15-minute agent-signed tokens in an httpOnly cookie
Operator funds Relayer hot wallets (EVM, Solana), the NEAR gas sponsor, the fee treasury
The published build The agent image digest and its code hash, approved on the vault

Trust boundaries

Diagram: Trust boundaries

The vault is the security boundary: it builds or strictly parses everything it signs, fixes every sweep destination, and enforces per-handle, per-token caps and delays on-chain. The agent is trusted to be honest within those limits, and attestation plus governance decide which builds count as the agent.

Threats

Compromised agent (bug or stolen key)

  • What it could do: ask the vault to sign anything the vault allows, for any handle still on the system key.
  • Mitigations: the vault builds every sweep and intent itself or parses 1Click messages strictly (contracts/vault/src/external.rs); sweep destinations are fixed (Omni calldata, or a registered address after a public 24-hour delay and within a daily cap); per-token daily caps and a 24-hour delay above the threshold; owner keys are always queued; the agent's NEAR key lives only in the TEE; the relayer takes an X ID, never an address, and has budgets; governance can trip the 72-hour circuit breaker at once, which stops withdrawals, intents and sweeps.
  • Residual risk: up to one day's cap per handle and token, across every system-key handle, before the breaker is tripped. Monitoring vault events and the new sweep-address audit (below) shorten that window. Self-custodied handles are out of reach.

Compromised TEE (hardware or firmware flaw)

  • Mitigations: registration requires an attested build with approved measurements and an approved CPU (PPID) and expires after 7 days; approving new builds or CPUs waits 14 days; governance can remove an agent instance at once and trip the breaker.
  • Residual risk: as for a compromised agent, bounded by the caps. Removing a PPID takes effect when the agent next calls the vault.

X account takeover before the owner signs in

  • What it could do: someone who takes over an X account can sign in and act as its owner.
  • Mitigations: withdrawals and ownership changes need a sign-in younger than 5 minutes; caps and the 24-hour delay apply; adding an owner key waits 24 hours in a public queue, and the real owner (or the agent) can cancel it; the X session token is revoked right after the one /2/users/me call.
  • Residual risk: the attacker can withdraw up to the day's cap. Owners who took full ownership (R7) are not affected. The Trust page says this plainly.

Phishing and lookalike handles

  • Mitigations: handles are resolved to numeric X IDs and addresses are derived from the ID, never from the typed name; lookalike characters, new accounts, protected accounts and recent renames show warnings on the profile card (packages/x, apps/web/components/ProfileCard.tsx); the card shows avatar, follower count and creation date before any payment; there is no "return to sender" (R3), so the FAQ says to check the card.
  • Residual risk: a convincing impersonator account with its own ID. The warnings help; they cannot decide for the sender.

Recycled handles

  • Mitigations: everything is keyed by the numeric ID; a handle that now points to a different ID shows a handle_recycled warning, and its addresses are those of the new ID (a new account never sees the old account's funds).

Replayed or expired requests

  • Mitigations: web → agent and sweeper → agent RPC is HMAC-signed over method, path, time, nonce and body hash, within 60 seconds and with a replay cache; every vault request carries the handle's nonce and an expiry of at most 10 minutes; intents carry versioned NEAR Intents nonces and deadlines of at most 30 minutes, and each nonce is accepted once; spend quotes are single-use and expire (apps/agent/src/intents/spends.ts).
  • Residual risk: the session revocation list is per agent instance, so a logged-out token stays valid on other instances until it expires (at most 15 minutes).

Sweep destination tampering

  • Mitigations: Omni routes write the destination into the calldata inside the vault; registered routes use the address registered for the handle, which only becomes usable after a public 24-hour delay and is capped per day; the vault derives Solana token accounts itself. New in this review: the sweeper, outside the TEE, compares every registered address of a watched handle with the bridge's own deposit_address answer every hour and raises an error-level alert on any difference (services/sweeper/src/audit.ts); the runbook's response is to trip the circuit breaker within the 24-hour delay.

Relayer drain

  • Mitigations: the relayer derives the target address from the X ID itself, so it can only pay handle addresses; per-funding and per-day budgets per chain; it is not reachable over the agent RPC; low-balance warnings and /ops balances; its keys exist only in the agent.
  • Residual risk: a compromised agent can spend the daily budget as gas into handle addresses (the gas stays with the handles).

X API credit drain

  • Mitigations: per-IP limits on handle lookups (20 per minute), 24-hour positive and 10-minute negative caches, debounced search, the app-wide 300-per-15-minutes budget watched on /ops. Fixed in this review: the handle page's metadata looked a handle up before the per-IP limit was checked, so crawlers could skip the limit; the lookup and the limit check are now one per-request step (apps/web/app/(site)/u/[handle]/page.tsx).
  • Residual risk: many IPs (a botnet) can still spend credits; X's own spending cap is the backstop (HANDOFF).

XSS

  • Mitigations: a per-request nonce CSP with strict-dynamic, no inline scripts, images only from our origin and X's CDN (apps/web/lib/csp.ts); React escapes all output and the code has no dangerouslySetInnerHTML or eval; X profile text is rendered as text.

CSRF

  • Mitigations: every non-GET API route checks Sec-Fetch-Site and Origin against our host (assertSameOrigin); session cookies are httpOnly, SameSite=Lax and Secure in production; the ops cookie is SameSite=Strict; OAuth state is sealed and bound.

SSRF

  • Mitigations: no server-side fetch takes a user-supplied URL. The agent and sweeper call fixed hosts from configuration (X API, 1Click, the PoA bridge, the Omni fee API, chain and NEAR RPCs); avatars are loaded by the browser, not the server; open-graph images do no lookups.

Session fixation

  • Mitigations: a new session token is minted at every sign-in; tokens are signed by the agent and never accepted from the URL; the pre-login OAuth state cookie is separate and single-use.

Dependency supply chain

  • Mitigations: frozen lockfile in CI; install scripts allowed only for an explicit list (pnpm-workspace.yaml allowBuilds); exact versions for security-relevant packages; the agent image is digest-pinned and reproducible; the one vendored crate is recorded in VENDORED.md; secret scanning in pre-commit and CI; new: CI fails on high or critical advisories in runtime dependencies (pnpm audit --prod --audit-level high).
  • Known advisories (not reachable): elliptic (low) through @near-js/crypto's secp256k1 path, which the agent does not use (its NEAR key is Ed25519); uuid and stream-json (moderate) through @solana/web3.js's RPC client inside @phala/dstack-sdk, which the agent does not call. Recheck at each release.

Logs leaking private data

  • Mitigations: the logger replaces sensitive fields by name (tokens, secrets, signatures, balances, amounts, recipients, senders, deposit and refund fields) and scrubs token-shaped values from every string, with tests (packages/logger); money services never log amounts, destinations or balances; X IDs appear only where the data is already public (vault events, sweep-address registrations); the 1Click session token is kept in memory only.

Also considered

  • Governance compromise: it can only propose (14-day public timelock) and reduce power at once (breaker, remove agents, cancel proposals, lock_forever). Use a multisig (HANDOFF).
  • MPC network failure: signatures stop; the vault rolls back the day's limits on failed signing; funds do not move.
  • 1Click or NEAR Intents failure: sends and spends stop; nothing is signed without a valid quote; public deposits stay on the handle's addresses until sweeps resume.
  • Testnet fixture controls: the simulated "pay" buttons exist only in the preview and on testnet fixture mode and move simulated balances only; mainnet refuses fixture mode.
  • Passkey-only owners: a passkey works only on this website, so it cannot replace the system key.

Fixed in this review

  1. Handle lookups in page metadata skipped the per-IP limit (X API credit drain).
  2. Nothing checked registered sweep addresses against the bridge during their delay: added the hourly audit in the sweeper, with a runbook entry.
  3. CI had no dependency audit: it now fails on high or critical advisories.

For the security audit

The vault contract (contracts/vault) and the agent's money services (apps/agent/src/intents, sweeps.ts, relayer.ts) carry the risk. Suggested focus: the strict parser for 1Click messages, the sweep planners and fee bounds, the cap and delay accounting, the governance timelock, and the attestation checks in the vendored shade-attestation.