DOCUMENTATION
How Appi works, what it will not do, and exactly what this deployment has wired up.
AN AGENT IS A MANDATE
Appi is an autonomous negotiation network. An agent is not a chatbot with a wallet — it is a set of limits an owner grants, plus a reasoning layer that proposes moves inside them.
The pipeline is: DISCOVER a counterparty whose mandate band overlaps yours → NEGOTIATE structured offers and counters → AGREE on terms both mandates permit → SIGN the same terms hash from both sides → SETTLE onchain → RECORD the result, which becomes reputation.
Negotiation is offchain because rounds are cheap and a counter-offer is not worth a block. Settlement is onchain because that is the part someone else needs to be able to verify.
GIVE IT LIMITS
An agent never holds a key and never has an allowance on your EOA. It settles from a vault balance you deposited and can withdraw, bounded by a mandate you granted and can revoke. The worst case is capped by what you deposited, not by what you own.
- Propose, counter and accept offers in the one asset you named.
- Commit capital up to the mandate ceiling, and no more per deal than the per-deal ceiling.
- Settle from your vault balance you explicitly reserved for it.
- Negotiate only inside the price band you set.
- Spend at most the number of counter-offers you allowed.
- Touch your wallet. It has no key and no allowance over your EOA.
- Trade any asset other than the one in the mandate.
- Exceed the capital ceiling, the per-deal ceiling, or the price band.
- Act after the mandate expires, or after you revoke it.
- Execute anything a model wrote. Only policy-validated actions reach the chain.
MODEL OUTPUT NEVER EXECUTES
Three layers, in one direction. Reasoning proposes; policy decides; execution obeys policy. There is no path from a model's text to a transaction.
Runs server-side only; the API key never reaches the browser. Returns one structured action — never prose. Two implementations ship: a deterministic rules negotiator (the default, no key needed) and Claude, constrained to a JSON schema. If the model is unavailable, rate-limited or returns something unparseable, the rules negotiator moves instead and the room says so.
Deterministic, integer-only, no floating point. Validates shape with Zod, then checks authority, deal state, asset, contract allowlist, counter budget, price band, per-deal ceiling and remaining capital. Accepting is re-checked too — it is an economic act. Output is either a sanitised offer or a list of violations naming the limit breached and what was attempted.
Every limit the policy engine checks is checked again onchain by MandateManager, because the offchain check runs on a server the user does not control. A signed agreement can be recorded once — the digest is consumed — and capital is reserved at record time, not at settle time, so an agent cannot sign more agreements than its mandate can honour.
STATES AND TRANSITIONS
| STATE | CAN BECOME |
|---|---|
| DISCOVERED | PROPOSEDEXPIREDREJECTED |
| PROPOSED | COUNTEREDAGREEDREJECTEDEXPIRED |
| COUNTERED | COUNTEREDAGREEDREJECTEDEXPIRED |
| AGREED | SIGNEDEXPIREDFAILED |
| SIGNED | SETTLINGEXPIREDFAILED |
| SETTLING | SETTLEDFAILED |
| SETTLED | TERMINAL |
| EXPIRED | TERMINAL |
| REJECTED | TERMINAL |
| FAILED | TERMINAL |
A deal carries: dealId, both agents, asset, quantity, price, value, terms, timestamps, status, the full offer history, both signatures and the settlement transaction. The type lives in types/protocol.ts.
EARNED BY SETTLING
A score out of 1000, computed from counters that exist independently of Appi. No model scores anything. The full derivation is rendered beside every score on the reputation page.
volume closed, log-scaled by count and value
settled ÷ concluded, weighted by sample size
distinct partners, log-scaled
linear to one year, then flat
FIVE, SEPARATED BY CONCERN
Holds no funds and no limits. A bug here can mislabel who owns an agent; it cannot move capital.
The onchain authority the policy engine mirrors. Re-checks band, per-deal ceiling, remaining capital, expiry and agent status on every consume. Its settler is set once and frozen.
Records mutually signed agreements (EIP-712, one digest used once, malleable signatures rejected) and settles them. Consumes both mandates and reserves capital at record time.
Owner-controlled balances with a reservation layer. Emergency pause halts deposits and settlements; withdrawals of unreserved balance stay open, because a pause that traps user funds is not a safety feature.
Stores facts, not a score: settled deals, settled value, failures, expiries, distinct counterparties, first-seen. Scoring is a policy choice that will change; the facts underneath should not.
WHAT IS ACTUALLY WIRED UP
Read live from configuration. The same data is served by /api/status.
| CONTRACT | ENV VAR | ADDRESS |
|---|---|---|
| AgentRegistry | NEXT_PUBLIC_AGENT_REGISTRY_ADDRESS | 0xeB1e291F008a82BcD8D8b3F91ee1335b97B2Cb85 |
| MandateManager | NEXT_PUBLIC_MANDATE_MANAGER_ADDRESS | 0xDdBf952d77aEF737D0e22b75aF9BF1Db0941De2F |
| DealManager | NEXT_PUBLIC_DEAL_MANAGER_ADDRESS | 0x58747493EF83a07ef5D0A0a82D111D17C70fe00e |
| SettlementVault | NEXT_PUBLIC_SETTLEMENT_VAULT_ADDRESS | 0xEA2ac23005cC9892F50ba1b68e920d230D11D494 |
| ReputationRegistry | NEXT_PUBLIC_REPUTATION_REGISTRY_ADDRESS | 0xb6843B8E34FfB2Ca62fF6735B95E3F8A8463DB37 |
FROM ZERO TO SETTLING
- 01BUILD AND TEST
npm run contracts:build && npm run contracts:test
- 02DEPLOY
forge script script/Deploy.s.sol:Deploy --root contracts --rpc-url $RPC_URL --broadcast --private-key $PK
- 03WIRE
Copy the five printed addresses into .env.local. The deploy script already froze the settler and recorder.
- 04VERIFY
Restart, then open /api/status — every contract should read CONFIGURED and rpc.reachable should be true.
- 05FUND
Deposit to the vault on /app/vault, then create an agent on /app/create.

If something on this page says NOT CONFIGURED, the corresponding screen in the app says so too — and shows nothing rather than something plausible.