Operating the network
The security budget only means something if the system it protects is running, watched and kept in good order; the person who gave a morning-brief agent a budget expects it to run at seven every morning without them. Deploying your own network is the playbook for standing up a copy, deploying.md has the hands-on commands, and SECURITY.md and ARCHITECTURE.md are the underlying records.
What runs on-chain
Everything the agent relies on to pay and to be protected lives in a small set of contracts.
AIOUMToken is ZAI, the stake and governance asset. AIOUMGovernor and the TimelockController
are governance: the Timelock owns every protocol contract and the Governor proposes.
StakingManager keeps share accounting, delegation, slashing and multi-asset rewards. The four
ERC-8004 registries hold identity, reputation, service and validation; the reputation registry is
where an agent's own verdicts land: one rating per settled session, 0–100, from the errors and slow
answers it saw, so the ratings the marketplace ranks by come from real results.
PaymentManager is the money path: prepaid sessions, EIP-712 vouchers (EOA and ERC-1271 signers
through one SignatureChecker path), the ETH / Permit2 / EIP-3009 funding paths, the fee split,
per-service splits (setServiceSplit) and result anchoring (commitResults, a root over the
served calls per session, each leaf keccak(index, requestHash, responseHash, toolIdHash)).
DisputeManager holds bonded disputes and arbitration, and its resolveStale(disputeId) lets
anyone close a dispute nobody ruled on within disputeTimeout, refunding the client and returning
the bond with no slash. The arbitrator is CommitRevealArbitrator, the open, staked commit-reveal
jury, installed as
DisputeManager.arbitrator() (ValidationQuorumArbitrator, its predecessor, remains deployable
with USE_QUORUM_ARBITRATOR). The ERC-4337 stack in src/aa/ is what lets a person hand an agent
a budget instead of keys: AIOUMAccount (budget-capped, expiring session keys; ERC-1271
vouchers), AIOUMAccountFactory (CREATE2) and AIOUMVerifyingPaymaster, the signature-gated gas
sponsor, on the chain's canonical EntryPoint (v0.7, 0x0000000071727De22E5E9d8BAf0edAc6f37da032).
DepositForwarderFactory gives every one of those accounts a deposit address, a CREATE2
DepositForwarder known before it is deployed, which forwards the USDC a route delivered there:
depositFeeBps to the treasury, the rest to the AI wallet, and anyone may sweep it.
Payments settle in the chain's USDC and USDT through their real receiveWithAuthorization and
Permit2 flows, with PaymentManager wired to the canonical Permit2
(0x000000000022D473030F116dDEE9F6B43aC78BA3). MockUSDC, the mock Permit2 and the open-mint
ZAI exist only on the local development network.
What runs off-chain
Around the contracts runs a small set of services, each taking a job off the person's plate.
Every one of them is a program anyone can start, and the app, the command line and Claude can be
pointed at any copy (Run every part yourself); none has to be ours, and
none is a single point the network depends on.
The node serves paid calls and settles them; the facilitator and bundler pay gas so an agent
never holds ETH; the deposit service turns whatever a person sends from any chain into USDC in
their AI wallet; the indexer answers the marketplace searches an agent makes on its own during a
run; the monitor watches all of it. A runner (offchain/src/agents/server.ts) runs each agent,
opens and settles its sessions, and files its ratings: each person's own inside their private
node, or a shared one people sign in to with their wallets.
| Service | What it does | How it runs |
|---|---|---|
Node runtime (npm run node, npm run multinode; offchain/src/node/server.ts) | Serves payment-gated tools over x402, accepts EOA and ERC-1271 vouchers, and commits result hashes before it settles. | Persists the latest voucher and results root per session to disk (AIOUM_NODE_DIR, default ~/.aioum-node; AIOUM_STORE=sqlite for a store shared between instances) and reloads them on boot; settles from a durable queue with fee-bumped retries. /health returns 503 when degraded, /live reports the process. Runs behind TLS. |
Facilitator (npm run facilitator, :8547; offchain/src/facilitator/server.ts) | Non-custodial relayer: submits openSessionWithAuthorization and pays the gas. Funds move payer → escrow, never through it. | Bearer auth, per-IP rate limit, request-size and validity-window bounds, asset allowlist, per-request amount ceiling, EIP-3009 nonce replay rejection, an in-flight cap, sanitized errors and stuck-tx fee bumps, all set through the FACILITATOR_* env in .env.example. Several instances run on distinct relayer keys behind a load balancer. The facilitator is optional: the SDK's agents open sessions through the bundler, and any client can open a session directly with a self-funded openSession* call. |
Bundler (npm run bundler, :8548) | Relays signed UserOps to the EntryPoint. | Relays only openSessionFor, with a per-account gas cap, gas-field validation, an expiry-based replay guard and fee-bumped replacement; several instances, like the facilitator. |
Sponsorship (AIOUMVerifyingPaymaster) | Pays gas for sponsored operations. | Sponsors only UserOps that carry a time-boxed signature from sponsorSigner; its owner keeps the EntryPoint deposit topped up. |
Deposit service (npm run deposits, :8551; offchain/src/deposits/server.ts) | Funds an AI wallet from any chain: chains, assets and routes to USDC from the route provider (LI.FI), a deposit address per AI wallet, a ledger of every deposit, and the loop that refreshes pending routes and sweeps what arrived (the conversion fee to the treasury, the rest to the wallet). | DEPOSITS_PRIVATE_KEY is the sweeper, a funded hot key that only pays gas for sweeps and never holds user funds; LIFI_API_KEY for the route provider; the ledger on persistent disk (AIOUM_DEPOSITS_DIR); loopback by default (AIOUM_DEPOSITS_HOST) and behind TLS when exposed. Serves the chains the provider delivers to; on any other chain it says so at start and credits only USDC sent straight to a deposit address. |
Staking node (npm run backer, :8552; offchain/src/backer/server.ts) | Backs a listing so a seller goes live with 100 ZAI of their own: reviews it for real (the listing on its node, the own floor, slashes and disputes, the price against the gallery, a sample call, a model against the rubric), delegates from its pool, and takes the ZAI back when the node degrades. | BACKER_PRIVATE_KEY holds the pool's ZAI, a little USDC for sample calls and gas; a reviewer (ANTHROPIC_API_KEY, an OpenAI-compatible endpoint, a local Ollama, or a judge listing in BACKER_REVIEW_SERVICE; without one it approves nothing); BACKER_MAX_PER_OPERATOR_ZAI caps one operator; the ledger on persistent disk (AIOUM_BACKER_DIR); loopback by default, behind TLS when exposed. |
Discovery indexer (npm run indexer, :8549; offchain/src/indexer/server.ts) | The marketplace the app, the agent runner and the SDK search; answers the free searches an agent makes on its own during a run. | Rescans the chain every AIOUM_INDEXER_REFRESH_MS and probes the nodes in AIOUM_INDEXER_SEEDS; behind TLS. |
Subgraph (subgraph/) | History and analytics over GraphQL for your own explorers and dashboards; the app's Activity tab and the explorer console read chain events and the indexer directly. | Entities Node, Service, Session, Delegation, Feedback, Reputation, Identity; runs on the self-host stack in subgraph/docker-compose.yml or on The Graph. |
Monitor (npm run monitor; offchain/src/monitor/watch.ts) | Alerting and metrics. | See below. Read-only: holds no keys and sends no transactions. |
Runner (offchain/src/agents/server.ts) | Runs each agent, project and pipeline, plans assistants, streams runs live and keeps the spend rollups. A person's own runs inside their private node and pays from the wallet connected there. A shared runner (AIOUM_AGENTS_MODE=shared) is a listing people sign in to: their own accounts pay, within a cap they sign; the runner's wallet pays for nothing of theirs. | Own: started by the private node, loopback, behind the node's token. Shared: AIOUM_AGENTS_KEYS_SECRET seals the spending keys at rest; records on persistent disk (AIOUM_AGENTS_DIR); behind TLS. Neither starts without a mode. |
Paid reviews (ReviewManager; AIOUM_AGENTS_REVIEWER=1 on a runner) | A lister deposits ZAI, the chain draws bonded reviewers by bond, each calls the listing from a fresh wallet and seals a score; the median is the result, and reviewers within tolerance are paid. | The contract computes the least a reviewer is paid from the listing's price, so a review never costs more than it pays; the reviewer's runner keeps the sealed scores' salts and the callers' keys under owner-only permissions. |
| Private node (:8770) | A user's own agent runtime: workspace, installed models, accounts, a wallet and an exchange with caps, memory, tools and scheduler. | Encrypted secrets and memory, loopback-bound, bearer token on every state-touching route; exposed only over TLS. |
| TLS (deploy/Caddyfile) | HTTPS at the edge. | Automatic certificates; the Node.js services speak plain HTTP behind it and, apart from the private node and the monitor, listen on every interface, so you keep the OS firewall closed to their ports. |
What runs on a host, and what never does
The catalogue is built for the machine it runs on. npm run hardware:check writes what the host
has, CPU, RAM, GPUs and VRAM, Docker and its NVIDIA runtime, Ollama, and the bootstrap lists a
local model or a GPU job only when the machine meets the template's requires, skipping the rest
with the reason and a pointer to the rented compute listings; nothing image-heavy lands on a CPU
box by accident, and the optimizer's settings are applied when a listing runs.
Third-party code never runs on a host outside a container. A compute listing runs a buyer's job
in docker run --rm with the buyer's files at /in, the output at /out, and the limits the
listing sets; a registry MCP server is verified only inside a Docker container with nothing of
the host mounted, and a server whose command is a binary on the host is not run by the check. An
MCP server that reads files, runs commands or drives a browser on the node it runs on is marked
hostAccess: never in a bootstrap catalogue (AIOUM_CATALOGUE_MCP=verified or all opts the
others in), and loaded only when the operator sets allowHost: true on that listing, a decision
made per listing and never by default. A skill's scripts run on the buyer's own private node, in
its workspace, never on the seller's.
Monitoring
Nobody has to sit and watch the network for the agent's seven o'clock run to happen.
npm run monitor subscribes to the events that matter and polls the services. On-chain it watches
for Slashed above MONITOR_SLASH_ALERT_WEI, for every DisputeRaised, for failed settle calls
(inspected MONITOR_CONFIRMATIONS blocks deep so a reorged fork raises no false alert), for
SessionOpened volume above MONITOR_SESSION_VOLUME_ALERT per poll window, and for Timelock
schedule and execute on TIMELOCK_ADDRESS. Among the services it polls the facilitator's and the
node's /health every MONITOR_HEALTH_POLL_MS and checks the paymaster's EntryPoint deposit
against MONITOR_PAYMASTER_MIN_DEPOSIT_WEI. Every alert is POSTed to ALERT_WEBHOOK_URL, and
/metrics serves Prometheus-style metrics, loopback-only by default (MONITOR_HOST) and
token-gated with MONITOR_SCRAPE_TOKEN when bound elsewhere.
Each service's /health is a readiness check (RPC reachable, operator stake or relayer balance
in place) and /live a liveness check, so a supervisor can restart a wedged instance and a load
balancer can drain a degraded one before an agent's call ever reaches it.
Parameters governance controls
The parameters governance prices against ZAI are not constants baked into bytecode. Every safety parameter is settable by the contract owner, the Timelock, so nothing is frozen at deploy and governance retunes the security budget as the network grows.
| Parameter | Where | Notes |
|---|---|---|
| Protocol fee | PaymentManager | 5% at deploy. Operators set their stakers' share per service with setServiceSplit. |
| Conversion fee | DepositForwarderFactory (DEPOSIT_FEE_BPS at deploy) | 1% by default, at most 10%; immutable in the factory, so a new rate is a new factory named in deployments.json. |
| Dispute timeout | DisputeManager.setDisputeTimeout | 3 days at deploy; after it, anyone can resolveStale. |
| Slash size | DisputeManager (SLASH_BPS at deploy) | max(fixed floor, 10% of the operator's stake at dispute-raise); 80% to the treasury, 20% to the client. |
| Escrow cap | PaymentManager (ESCROW_CAP_USDC at deploy) | How much client money a node may hold, as a share of its stake per asset (about 50%). |
| Minimum validator stake | ValidationRegistry.setMinValidatorStake | |
| Jury | CommitRevealArbitrator | Quorum, minimum stake, commit and reveal windows, unbonding period. |
| Governor | AIOUMGovernor | Quorum 4%, about a one-day voting delay and a seven-day voting period. |
Upgrades and governance
A change to any of those parameters, and any re-wiring of the contracts (setRewarder,
setDisputeManager, setRewardAsset, setPaymentManager, setArbitrator), is a proposal to
AIOUMGovernor, queued and executed by the TimelockController after its delay, so a person
whose agent depends on the network sees every change coming. During bootstrap the guardian
multisig holds the Timelock's proposer and canceller roles as an emergency veto; control widens
to the full Governor on milestones governance sets and publishes. The contracts are single-step
Ownable: ownership moves once, from the deployer to the Timelock (npm run handover), never
to 0x0, which would freeze every setter. npm run verify-deploy exits non-zero unless the
Timelock owns every governance contract, the arbitrator and treasury are the configured
addresses, Permit2 and the EntryPoint are canonical, and no accept-everything paymaster holds a
deposit.
Keys
The point of AIOUM, from the person's side, is that they stop generating and guarding keys for every service their agent uses. The keys that remain are few, each has one role, and each is handled so a leak is bounded.
| Key | Role | Handling |
|---|---|---|
| Deployer | one-time deploy | Hardware wallet or multisig; ownership handed to the Timelock after deploy and the Timelock admin role renounced. |
| Facilitator / bundler relayers | gas-only, non-custodial | Funded hot keys (FACILITATOR_PRIVATE_KEY, BUNDLER_PRIVATE_KEY, or a shared RELAYER_PRIVATE_KEY); rotated; rate-limited; one per instance. |
| Sponsor signer | signs gas sponsorships | SPONSOR_SIGNER_ADDRESS at deploy; the paymaster owner can rotate it, which invalidates every outstanding sponsorship at once. |
| Paymaster owner | funds gas sponsorship | Multisig; keeps the EntryPoint deposit funded. |
| Deposit sweeper | pays gas for sweeps | DEPOSITS_PRIVATE_KEY; a funded hot key that holds no user funds, since a forwarder pays only the treasury and the wallet; rotated like the relayers. |
| Node operator | stake + settle | A keystore or OPERATOR_PRIVATE_KEY; the operator's own responsibility; capacity equals stake. |
| User session keys | per-call signing | Hot, capped and expiring: spendCap and validUntil are enforced on-chain, revokeSessionKey ends one early, and the app keeps them in memory, never in localStorage. |
| Private-node passphrase | encrypts secrets and memory | PRIVATE_NODE_PASSPHRASE, or an auto-generated 0600 .secretkey; user-held; never logged. |
Verification
The money path is backed by a test campaign that runs on every change. forge test covers the
contracts, 226 tests across 11 suites, including fuzz and StdInvariant campaigns in
Fuzz.t.sol: StakingFuzzTest (slashing conserves value and never
underflows; reward accounting never inflates, accounted ≤ distributed) and
PaymentInvariantTest (invariant_perAssetSolvency, invariant_noOverpay,
invariant_splitConservation, invariant_activeSessionCount), which drives a real
PaymentManager through randomized open/settle/reclaim/dispute/resolve sequences across three
assets. Static analysis and its triage are in
SECURITY.md, the off-chain boundary suites run with
cd offchain && npm run test:boundary, and
Security review records each hardening finding and its fix.
See also: SECURITY.md · ARCHITECTURE.md · frontend-and-ui.md
Next: What is kept, and where — every piece of state, where it lives, who writes it and what it survives.