@finchip.ai/foga 0.6.0-alpha.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +35 -14
  2. package/dist/a2a/card.js +54 -0
  3. package/dist/a2a/server.js +276 -0
  4. package/dist/a2a/types.js +20 -0
  5. package/dist/build/bundle.js +2 -1
  6. package/dist/cli-cell.js +176 -16
  7. package/dist/cli.js +150 -5
  8. package/dist/core/world.js +17 -0
  9. package/dist/demo/birth.js +11 -5
  10. package/dist/demo/loop-d.js +39 -1
  11. package/dist/demo/quickstart.js +97 -11
  12. package/dist/genesis/operator-scope.js +1 -0
  13. package/dist/genesis/probe.js +23 -7
  14. package/dist/http/access.js +105 -0
  15. package/dist/http/server.js +84 -43
  16. package/dist/http/signed.js +136 -0
  17. package/dist/mcp/server.js +48 -1
  18. package/dist/node/advise.js +134 -0
  19. package/dist/node/config.js +55 -1
  20. package/dist/node/documents.js +298 -0
  21. package/dist/node/preflight.js +208 -0
  22. package/dist/node/propagation.js +95 -0
  23. package/dist/node/wallet.js +132 -0
  24. package/dist/tools/documents.js +74 -0
  25. package/dist/tools/index.js +3 -1
  26. package/dist/tools/propagation.js +35 -0
  27. package/dist/tools/tenders.js +22 -6
  28. package/dist/util/version.js +2 -0
  29. package/dist/world/sourcify.js +165 -0
  30. package/docs/a2a.md +31 -0
  31. package/docs/design-memo.md +23 -6
  32. package/docs/documents.md +25 -0
  33. package/docs/examples/README.md +2 -2
  34. package/docs/extending.md +49 -0
  35. package/docs/loop-d.md +11 -0
  36. package/docs/mcp.md +18 -11
  37. package/docs/node.md +35 -11
  38. package/docs/og-deployment.md +131 -0
  39. package/docs/propagation.md +41 -0
  40. package/docs/quickstart.md +36 -21
  41. package/docs/wallet.md +62 -0
  42. package/docs/world.md +12 -0
  43. package/package.json +1 -1
  44. package/server.json +2 -2
@@ -1,6 +1,6 @@
1
- # FOGA design memo (v0.2, condensed)
1
+ # FOGA design memo (v0.4, condensed)
2
2
 
3
- This is the English working version of the design. Phase 1 (the world layer), Phase 2a (cells, kernel, genesis T0–T5), Phase 2b (the predicate ZK path, T6–T9, measurement receipts, doctor layer 5; docs/genesis.md) and Phase 2c (the public-chain path and the Sepolia run) implement its first slices; Phase 3 (section 9) turns the kernel into a node other agents can call. It records decisions, not aspirations: where something is still to be built, it says so.
3
+ This is the English working version of the design. Phase 1 (the world layer), Phase 2a (cells, kernel, genesis T0–T5), Phase 2b (the predicate ZK path, T6–T9, measurement receipts, doctor layer 5; docs/genesis.md) and Phase 2c (the public-chain path and the Sepolia run) implement its first slices; Phase 3 (section 9) turns the kernel into a node other agents can call, and section 10 records the three defaults settled while doing so. It records decisions, not aspirations: where something is still to be built, it says so.
4
4
 
5
5
  ## 1. Definition and scope
6
6
 
@@ -44,6 +44,7 @@ The "cellular automaton" idea is the architectural inspiration: each cell is an
44
44
  | operator | hot signer held by the node process, and the only key that process holds | calls only through `FogaCellAccount.execute`, within the owner's allow-list of (target, selector) pairs — never an asset-moving selector —, a daily value cap and an expiry; signs for the account only when the owner enabled it (account revision 2, 0.6.0; docs/cell-account.md) |
45
45
  | owner | cold key | rotates the operator, sets its allow-list, sets the guardian, recovers, moves assets |
46
46
  | guardian | ASF intervener | suspends, resumes, terminates the cell's binding |
47
+ | a wallet | by default one key — the agent's identity, which fills every role above — made by the agent on its own machine (`foga wallet new`, 0.6.0; `--cell` makes the separated owner/operator/guardian set as the guarded profile's opt-in hardening; docs/wallet.md) | the identity a cell is born with, and the credential a caller proves itself with to any cell (an EIP-712 signature in that cell's domain); made by default, never copied, never a reason to stop |
47
48
  | judges J1–J3 | EOAs (allowlist in v1; KYA-gated profile later) | voters of the ACDF policy FOGA-Council; never parties to the case |
48
49
  | evaluator E | EOA | asserts AUF measurement statements |
49
50
  | demander, buyer | B and W (tenders); C (service purchase) | funds tenders; deposits into the service escrow |
@@ -79,7 +80,7 @@ Per ERC: the minimum run acceptance in docs/compatibility-matrix.md. Loop C case
79
80
 
80
81
  ## 7. MCP, A2A and pricing boundaries
81
82
 
82
- One kernel, three entrances; switching entrance never changes authorization rules or task state. MCP (2026-07-28) is stateless; business operations are persistent and addressed by `operationId`; long tasks use FOGA's own start / query / cancel / receipt before any protocol extension. Two modes: embedded (the FOGA harness runs the node) and host (an external agent uses the capabilities through MCP and owns scheduling, state, authorization and recovery). The free base tier is a complete self-hosted loop over the ten ERCs — node software, adapters and acceptance tools, doctor and probe, `spec.read`, local signing and evidence, unsigned calldata for writes; the paid tier covers hosted resources — runs and queues, indexing and history, proof computation, metered model / RPC / gas, bounded standard workflows. Call permission, business authorization and wallet permission are three different checks; paid-tool execution order is validate (including header/body consistency) → operator and permission → quote and payment → bind operation → idempotent execute → receipt; payment dedup and business dedup are separate. "$1" is a prepaid credit starting point and, for fixed-scope jobs, a capped trial package with explicit scope, budget caps, gas inclusion, failure/cancel/timeout billing and reserve/settle timing; x402 is a later payment adapter. An A2A task enters as a FOGA operation and becomes or binds a tender only when the caller chooses tender mode and every term is explicit; the agent card lists enabled, executable capabilities, not held tokens.
83
+ One kernel, three entrances; switching entrance never changes authorization rules or task state. MCP (2026-07-28) is stateless; business operations are persistent and addressed by `operationId`; long tasks use FOGA's own start / query / cancel / receipt before any protocol extension. Two modes: embedded (the FOGA harness runs the node with a model: `foga node run`) and host (the node is served and external agents use the capabilities through MCP, HTTP and A2A, owning their own scheduling, state and recovery: `foga node serve`); the OG instance runs in host mode by default and in embedded mode on demand (section 10). The free base tier is a complete self-hosted loop over the ten ERCs — node software, adapters and acceptance tools, doctor and probe, `spec.read`, local signing and evidence, unsigned calldata for writes; the paid tier covers hosted resources — runs and queues, indexing and history, proof computation, metered model / RPC / gas, bounded standard workflows. Call permission, business authorization and wallet permission are three different checks; paid-tool execution order is validate (including header/body consistency) → operator and permission → quote and payment → bind operation → idempotent execute → receipt; payment dedup and business dedup are separate. "$1" is a prepaid credit starting point and, for fixed-scope jobs, a capped trial package with explicit scope, budget caps, gas inclusion, failure/cancel/timeout billing and reserve/settle timing; x402 is a later payment adapter. An A2A task enters as a FOGA operation and becomes or binds a tender only when the caller chooses tender mode and every term is explicit; the agent card lists enabled, executable capabilities, not held tokens. A caller's identity is the same on every entrance: nothing (the public), a key the operator issued, or the caller's own wallet signing in the cell's domain (`FOGA-EIP712`, 0.6.0) — the general form of what A2A asks of a signed message, applied to `/api`, `/mcp` and `/a2a` alike, with the cell's own policy (`access.signers`) deciding what an address, an ERC-8004 agent or a KYA standing is granted.
83
84
 
84
85
  ## 8. Phases
85
86
 
@@ -89,13 +90,13 @@ One kernel, three entrances; switching entrance never changes authorization rule
89
90
  | 2a cells | `foga-core`, `foga-kernel`, `FogaCellAccount`; T0–T5 on Anvil; first loop-C cases. **Done: 2a-1 (0.2.0) account, core, kernel, T0–T3, four loop-C cases; 2a-2 (0.3.0-alpha.1) T4, T5, the credential-pattern proof for A, the FOGA-Council.** | T0–T5 green; restart recovery and dedup pass |
90
91
  | 2b cells | predicate circuit and adapter; T6–T9; measurement receipts; remaining loop-C cases; doctor layer 5. **Done (0.4.0-alpha.1): `kya_predicate_count_v1` with its development ceremony and FOGA's adapter, S_pred proved at the end of T3; T6 dispute with the refund enacted; T7 stop, bypass, attribution, escape attempts and the STALE gate; T8 receipts and resolver; T9 migration, skill adoption and non-inheritance; doctor layer 5 from the evidence.** | T0–T9 green; every negative case rejected as specified |
91
92
  | 2c public evidence | Sepolia run from one driver script; cells bound in both AID registries; FOGA-owned modules, adapter and escrow; evidence and Sourcify. **Done (0.5.0): attach-mode worlds completed from the bundle by `foga world deploy` (the revision-4 ERC-8471 stack and the two eligibility modules), twelve keys from one phrase with W as treasury (`foga keys`, step-wise top-ups, `foga genesis preflight`), the backing AID registry, the uniform T7 liveness demonstration, fee replacement for stuck transactions, reads pinned to the blocks the kernel has written, the public timing profile, the path rehearsed on anvil in CI; the Sepolia run of 2026-11-01 — world `sepolia-11155111-20261101T043153Z`, 203 operations, doctor Pass 234 with one known Fail, all eleven standards Pass — and its three files in `deployments/` (docs/genesis.md, "The Sepolia run"). Sourcify verification of FOGA's 15 contracts is still open.** | public state re-checkable; known mismatches reported |
92
- | 3 interfaces | the existing kernel completed with the permission, concurrency and recovery semantics a public service needs, then base MCP (stdio and Streamable HTTP), CLI, HTTP, A2A card and inbound mapping, documents from one config, ERC-8004 and ARD registration, MCP registry listing, documentation site; loop D. **Done: 3a-1 (0.6.0-alpha.1) kernel hardening; 3a-2 (0.6.0-alpha.2) the capability catalog, policy engine, decision records, node runtime and CLI, with the Loop D cases a–g driven through the CLI on anvil (section 9, docs/node.md). Next: 3b.** | published capabilities match acceptance results; external clients work |
93
+ | 3 interfaces | the existing kernel completed with the permission, concurrency and recovery semantics a public service needs, then base MCP (stdio and Streamable HTTP), CLI, HTTP, A2A card and inbound mapping, documents from one config, ERC-8004 and ARD registration, MCP registry listing, documentation site; loop D. **Done: 3a-1 (0.6.0-alpha.1) kernel hardening; 3a-2 (0.6.0-alpha.2) the capability catalog, policy engine, decision records, node runtime and CLI, with the Loop D cases a–g driven through the CLI on anvil; 3b (0.6.0-alpha.3) MCP and HTTP; 3c (0.6.0-alpha.4) Loop D with a model; 3d-1 (0.6.0-alpha.6) the package on npm, `foga quickstart`, an outside agent's Loop D; 3d-2 (0.6.0-alpha.7) the wallet, signed callers, the documents on chain, preflight, A2A, Sourcify; 3d-3 (0.6.0) the OG instance served at `foga.finchip.ai` (systemd and Caddy, host mode), listed in the MCP registry as `io.github.garyyang-finchip/foga`, its fifteen Sepolia contracts Sourcify-verified, and an outside agent's Loop D on Sepolia with every invariant row Pass (section 9, docs/node.md, docs/og-deployment.md, docs/loop-d.md). Phase 3 closed.** | published capabilities match acceptance results; external clients work |
93
94
  | 4 hosted | indexing, proof runs, scenario runner, gas sponsorship; `operationId` and idempotency; billing units; x402 on Base | measured costs; packages match behavior |
94
95
  | 5 replication and outreach | multi-arch images; independent-node replication; central-MCP-off test; discovery materials | an external operator completes the core scenarios alone |
95
96
 
96
97
  ## 9. Phase 3 — from kernel to node
97
98
 
98
- The premise of Phase 3 is not a new system but the kernel reused: Phase 2 built an idempotent, resumable driver for one scenario; a node that answers other agents needs the same kernel with three things it did not need as a driver — a permission model for a key it does not own, concurrency between callers, and recovery semantics a caller can rely on. Phase 3 is cut in four slices: **3a** the kernel completed (3a-1 below) and the capability catalog, policy engine, decision records and node skeleton (3a-2); **3b** MCP (stdio and Streamable HTTP, the 2026-07-28 specification) and the HTTP API with the npm package and registry listing; **3c** loop D — the demander, the model adapters, the unscripted run — as a scenario the doctor can judge; **3d** A2A, the documentation site, the OG instance's move to a revision-2 account and its public endpoints, the Sepolia evidence. The gate of each slice is a green that the next slice builds on; an always-on node holding a key runs only after 3a's acceptance passes.
99
+ The premise of Phase 3 is not a new system but the kernel reused: Phase 2 built an idempotent, resumable driver for one scenario; a node that answers other agents needs the same kernel with three things it did not need as a driver — a permission model for a key it does not own, concurrency between callers, and recovery semantics a caller can rely on. Phase 3 is cut in four slices: **3a** the kernel completed (3a-1 below) and the capability catalog, policy engine, decision records and node skeleton (3a-2); **3b** MCP (stdio and Streamable HTTP, the 2026-07-28 specification) and the HTTP API with the npm package and registry listing; **3c** loop D — the demander, the model adapters, the unscripted run — as a scenario the doctor can judge; **3d** in three parts — the package in use (3d-1), the cell among other agents: wallet, signed callers, documents, preflight, A2A, Sourcify (3d-2), and the OG instance's move to a revision-2 account with its public endpoints, the documentation site, the registry listing and the Sepolia evidence (3d-3). The gate of each slice is a green that the next slice builds on; an always-on node holding a key runs only after 3a's acceptance passes.
99
100
 
100
101
  Three checks, three vocabularies. A call to the node passes **call permission** (may this caller invoke this capability: public, caller-bound or operator-only), **business authorization** (does the cell do this, for this caller, under its budget and policy) and **wallet permission** (does the account let the operator make this call: the allow-list, the asset refusal, the daily cap, the gate). The three are different checks in different places — the node, the policy engine, the contract — and none substitutes for another. Capabilities are catalogued along three dimensions: action (read / build / act / admin), visibility (public / caller / operator) and principal (whose act it is); `binding_present` is admin, a build is for someone, an act is by the cell.
101
102
 
@@ -110,6 +111,22 @@ Three checks, three vocabularies. A call to the node passes **call permission**
110
111
 
111
112
  **3b, MCP and HTTP (0.6.0-alpha.3).** The six points written down for this slice, as they landed. One: the server (`@modelcontextprotocol/server` 2.4.0) serves the 2026-07-28 revision as its main path and the 2025-era handshake through the SDK's stateless compatibility layer, on Streamable HTTP and on stdio alike; `test/mcp.test.ts` drives it with the reference client pinned to 2026-07-28, in legacy mode and in auto mode, because a client that defaults to the old handshake proves only the old path. Two: annotations are static, so a write capability is `<name>` (act), `<name>_simulate` and `<name>_build` over one handler; the tool list is the catalog filtered by the caller, every call still goes through `Catalog.invoke`, and the number of tools (11 for the public, 36 for an `act` key, 40 for the operator) is not the number of capabilities (29, `spec_read` added). Three: `tender_precheck` answers `eligible: false` as an ordinary result; a refused act is `isError: true` with gate, reason and decision id; a credential the node does not know is 401 and a capability or mode above the caller is 403, never a business refusal. Four: no CORS headers, and a foreign `Origin` or `Host` is refused at the edge; the SDK decides the semantics of `GET /mcp` by era; bodies are capped and parsed by the capability's schema alone. Five: API keys (`foga node key new`, stored as SHA-256, compared in constant time) are for the operator's own integrations and named partners; the public reads and builds; OAuth stays out of Phase 3. Six: `scripts/pack-smoke.mjs` installs the `npm pack` tarball in a clean directory and runs the packaged CLI from there — a cell born, `foga node mcp` without a key driven by a raw 2025-era client over stdio, stdout protocol-only, `spec_read` served from inside the package — as a CI step; `engines.node` is `>=22.13`, where `node:sqlite` needs no flag. Two things the slice found: the request hash that binds a job excludes the idempotency key itself (a request first made under a derived job id and then repeated naming that id is the same request), and a cell's birth adopts the FOGA-Cell-v1 specification its owner published for an earlier cell instead of failing on `SpecificationExists`. The read-only configuration of the OG instance (`deployments/sepolia.cell.example.json`), `package.json`'s `mcpName` and `server.json` are in place for the publication in 3d. docs/mcp.md.
112
113
 
114
+ **3c, Loop D (0.6.0-alpha.4).** The runner (`src/node/runner.ts`) is the loop of section 8 as written: perceive through the read capabilities, propose with the catalog as tools, decide in `Catalog.invoke`, execute as keyed operations, record by hash — 12 tool calls and 2 acts a tick, the model's calls counted against the day, the watchers outside the model. One adapter interface in the Messages API's vocabulary; `anthropic` over `fetch` with the key in the environment and the model name in the configuration, `stub` as the deterministic table that also makes the over-budget proposal of case g. The demander is its own process with its own key and store; the cases a–f are minted in a seeded order and carry what the acceptance expects. `foga demo --loop-d` runs it all on a new cell with keys nobody held — the kill after the first submission and the restart, the guardian's suspension for f, the other entrances beside the runner — and `foga doctor --loop-d` judges the evidence beside layer 5 with the rows of section 8's matrix and the eight additions of v0.2 (the same request across entrances, the same job under another request, concurrent acts with distinct nonces and the budget holding, the recovery, the operator key's transactions read inside `execute`). The run found a race the matrix was written to find: three concurrent mints each passed the budget gate because none saw the others' reservations, and the account's daily cap refused the third on chain; the decision section now runs one act at a time per node and a job reserves its planned value from its decision, so the refusal is the node's, at `budget`, before anything is sent. It also found that an `execute`'s inner value was not what the operation's row recorded (the outer transaction's zero was), so the value book undercounted; fixed. Stub and real model run the same matrix; the real model's runs (anvil, then Sepolia) are 3d's evidence. docs/loop-d.md.
115
+
113
116
  **3d-1, the package in use (0.6.0-alpha.6).** The npm package went out as `@finchip.ai/foga` (alpha.4, then alpha.5 with the launcher's Windows fix: a drive-letter path is not a URL), which made the first slice of 3d the one that lets a stranger use it: `foga quickstart` — anvil, world, a cell with fresh keys and its skills, two tenders, the served node, both entrances checked, the client configurations printed with the paths filled in — and Loop D with an outside proposer (`--model external`), where the node is served rather than run and whoever holds the printed act-level key works the tenders through `/mcp` or `/api` while the orchestrator does what it does for a model: the kill and restart, the entrance checks, the suspension, the evidence, the doctor's rows (an untouched case `NotTested`, the comparison over the cases both runs reached). This is the shape the Sepolia run of section 11 will take: the operator's own agent, on its own machine, with whatever model it has, judged by the same matrix as the stub. The slice also made the cell's birth a shared routine (`src/demo/birth.ts`) for the demos, fixed line endings for every platform (`.gitattributes`) and let a git install compile itself (`prepare`). docs/quickstart.md, docs/loop-d.md.
114
117
 
115
- **3c, Loop D (0.6.0-alpha.4).** The runner (`src/node/runner.ts`) is the loop of section 8 as written: perceive through the read capabilities, propose with the catalog as tools, decide in `Catalog.invoke`, execute as keyed operations, record by hash — 12 tool calls and 2 acts a tick, the model's calls counted against the day, the watchers outside the model. One adapter interface in the Messages API's vocabulary; `anthropic` over `fetch` with the key in the environment and the model name in the configuration, `stub` as the deterministic table that also makes the over-budget proposal of case g. The demander is its own process with its own key and store; the cases a–f are minted in a seeded order and carry what the acceptance expects. `foga demo --loop-d` runs it all on a new cell with keys nobody held — the kill after the first submission and the restart, the guardian's suspension for f, the other entrances beside the runner — and `foga doctor --loop-d` judges the evidence beside layer 5 with the rows of section 8's matrix and the eight additions of v0.2 (the same request across entrances, the same job under another request, concurrent acts with distinct nonces and the budget holding, the recovery, the operator key's transactions read inside `execute`). The run found a race the matrix was written to find: three concurrent mints each passed the budget gate because none saw the others' reservations, and the account's daily cap refused the third on chain; the decision section now runs one act at a time per node and a job reserves its planned value from its decision, so the refusal is the node's, at `budget`, before anything is sent. It also found that an `execute`'s inner value was not what the operation's row recorded (the outer transaction's zero was), so the value book undercounted; fixed. Stub and real model run the same matrix; the real model's runs (anvil, then Sepolia) are 3d's evidence. docs/loop-d.md.
118
+ **3d-2, the cell among other agents (0.6.0-alpha.7).** Six pieces, one principle (section 10). *The wallet.* `foga wallet new` makes an agent's keys on its own machine — by default one key, its identity, which fills every role a cell needs (the separated owner/operator/guardian set is the opt-in `--cell` hardening) — in 0600 files, with no network and no chain; it never overwrites, and whatever needs the keys goes on with keys made in memory when the disk refuses (`foga quickstart` makes the default wallet on its first run and births every later cell with it; `--wallet <dir>` on every `foga cell` and `foga node` command takes the role's key from there). *Signed callers.* `Authorization: FOGA-EIP712 <envelope>` is EIP-712 typed data in the cell's own domain (name FOGA, version 1, the chain, the cell's account): a `Request` envelope bound to method, path and body hash, good once within 120 s, or a `Session` envelope, reusable within 600 s for a client with static headers; the caller is the signing address and `access.signers` says what it is granted — the cell's own operator or owner remotely, an allow-list by address or by ERC-8004 agent id (checked against the registry's owner and agent wallet), a KYA standing threshold, a default of build, read or none; an act wants a request-bound envelope unless the policy says otherwise. `/api`, `/mcp` and `/a2a` take the same header; `GET /api/capabilities` tells a caller what it is. *The documents.* `foga cell docs build | publish | status` generates from one configuration the ERC-8004 registration (`registration-v1`, with `registrations`), the ERC-8434 AID document (identity, standing, skills and endpoints facets), the ERC-8444 descriptor, the ERC-8454 unit definition, the A2A agent card, the registry's `server.json` and a manifest of content hashes; the node serves them under `/documents` and `/.well-known`; `docs_publish` (the 31st capability, the operator's act) commits only what differs from the chain — `setAgentURI`, `setDocumentURI`, `updateDescriptor`, `publishVersion`, as one keyed job — and reads every commitment back; a cell without a public URL commits `data:` URIs and is complete on chain all the same. *Preflight.* `foga node preflight` reconciles a cell against the chain and the machine before it is served — the world, the account's revision, roles and allow-list, the budget, the operator key alone and its gas, the standing, the skills' packages and their executors actually run, the documents, the public URL, the model — as rows that are advisory by default and fatal with `--strict`. *A2A.* `POST /a2a` is the 1.0 JSON-RPC binding with the card at `/.well-known/agent-card.json`: a quote for anyone (the offered skills and the terms a tender needs to meet), a tender taken for an act-level caller as one task over the catalog's jobs (pre-check, run, submit; settlement stays the watchers'), `GetTask`, `ListTasks` scoped to the caller, `CancelTask` that undoes nothing broadcast, duplicates by (caller, messageId), the specification's error codes; no road does work first and talks about payment after. *Sourcify.* `foga world verify --sourcify` submits every FOGA-deployed contract of a world with the standard JSON input of its own compiler run — `forge build` leaves the build-info beside the artifacts — matched by runtime code and identified by the path solc saw; a local chain is a dry run and CI dry-runs all 47; the Sepolia run was 3d-3's (below: fifteen contracts, 2 exact_match and 13 match). The gate: `test/wallet.test.ts`, `test/signed.test.ts`, `test/documents.test.ts`, `test/a2a.test.ts`, `test/preflight.test.ts`, `test/sourcify.test.ts`, and `foga quickstart` ending with a cell whose documents are on chain, whose card answers and whose A2A entrance quotes. docs/wallet.md, docs/documents.md, docs/a2a.md, docs/node.md ("Preflight"), docs/world.md ("Verifying the sources").
119
+
120
+ **3d-3, the OG live and the public-chain evidence (0.6.0).** The reference cell is served: born fresh on Sepolia by the same path any cell takes (`wallet new`, `cell new`, `cell init`, `docs publish`, `node serve` — one key, no migration, no key handoff), agent 11261, account `0xD7155E48…`, its skill `finch-extract-verify` offered, its documents on chain, its fifteen FOGA-deployed contracts Sourcify-verified; served in host mode under systemd behind Caddy at `foga.finchip.ai`, so `/healthz`, `/.well-known/foga-cell.json`, `/.well-known/agent-card.json` and `/mcp` answer from outside and survive a reboot; listed in the MCP registry as `io.github.garyyang-finchip/foga` with the npm package as the stdio server and the public URL as the remote. The last acceptance ran on the public chain: Loop D with an outside agent (`--model external`, a person driving the HTTP entrance with the agent key), a second cell born for it (agent 11264), the demander's cases minted on Sepolia — and every invariant row Pass (every operation under an allowed decision; eleven refusals across policy, verifier, skill, conflict, budget, safety and execution with nothing broadcast; the four budget books; sixteen operator transactions at the account, allow-listed, none reverted; the recovery drill across a kill and restart with nothing sent twice; idempotency; conflict; concurrency), cases a, c, d, d2 and e as expected. The run also returned four findings a local chain cannot produce, each closed in 0.6.0 (docs/loop-d.md, "On a public chain"): a working directory reused across runs replayed the previous run's keyed operations (the orchestrator now clears its own files); the roles' gas reserve, priced at a near-zero gas price, left the operator under the policy's gas floor (the reserve is floored at 1 gwei); the settlement figure read as the account's balance delta took in a concurrent mint's escrow (it is now the tender's own: escrow released less credit left); a 9000-block log scan overran the worker's minute on a shared endpoint (the scan is windowed, the public-chain budget three minutes). docs/og-deployment.md, docs/loop-d.md.
121
+
122
+ ## 10. Five defaults settled in 3d
123
+
124
+ **The wallet is the first step, made by default, never a reason to stop — and it is one key.** Every agent that touches FOGA — one that will be a cell, one that only calls cells — begins by making its own key on its own machine; the install path puts it first and expects it to succeed almost always, and when it does not (a disk that refuses the write) the next step is taken with a key made in memory, said plainly, and nothing is retraced. The base is a single key, the agent's identity, and that one key fills every role a cell's account needs (a cell born with it has the same address as owner, operator and guardian). The owner/operator/guardian split — a cold owner, a hot operator, a separate guardian — is the `FogaCellAccount` guarded profile's hardening, not a requirement of being covenant-complete; it is opt-in (`foga wallet new --cell`), for a cell that runs real value. This is the correction Victor's question forced: the three roles were an operational habit from the first EC2 cell, not a property of the ten covenants, and nothing covenant-facing (the MCP base, `become-a-cell`) should present more than one key as required. The wallet is optional in the one sense that matters: its absence blocks nothing a caller could do before. The three 3d-2 standards that rest on it — `foga wallet new`, signed calls, `foga node preflight` — are standards for a cell, never gates on the agents that connect to one.
125
+
126
+ **Identity is one's own; only the genome replicates.** A cell that meets another cell — an agent that connects to a cell's MCP entrance and reads the prompt `become-a-cell` — can make itself a cell: the wallet, `foga quickstart`, then `foga cell new --wallet` and `foga cell init --wallet` on a public chain. What it copies is the program: the package, the catalog, the acceptance. What it makes is its own: the keys, the account, the agent id, the standing an issuer gives it. Nothing of the first cell's identity is copied or inherited, and the relation between the two afterwards is between peers on the chain — tenders minted and taken, swarms joined — not between a model and its copies. The OG instance is the first cell and the standing example, not the parent.
127
+
128
+ **The OG instance is served by default and alive on demand.** Host mode (`foga node serve`: every entrance, the watchers keeping the cell's commitments, no model) is the OG's resting state, costs nothing to think, and lets any agent in the world use the cell's capabilities and the catalog's judgment; embedded mode (`foga node run`, the runner with a model proposing acts) is started for a Loop D run or a period of autonomous work and stopped after. A cell is portable in the same breath: the configuration, the store and the operator's key file are the whole of it, and moving them to another machine moves the cell. Being a cell does not require being always on; it requires that whatever the cell commits to is kept, and the watchers keep it.
129
+
130
+ **The first run is minimal; the hardening is advice.** What it takes to be a cell — one key, the defaults, no public face — is all the first run asks, and it stays that way on purpose: becoming a cell must never be a project, or the next cell never comes. What a cell should do *next* is said after it runs, not before: `foga cell advise` reads the configuration and names only what applies — separate the one key into owner, operator and guardian (the serving key bounded, the owner cold, a guardian of its own), a policy at its defaults, standing, a public face, a supervisor, backups, a dedicated endpoint — with why and how; the recommended items are printed in brief after `foga quickstart`, after `foga cell init` on a public chain and once at `foga node serve`. Advice, never a gate: a cell is complete without any of it. The sequence is the one the project itself took — one key for speed, the separated set when it mattered — and the separation of the three powers is sound exactly to the degree it is used: an owner that stays cold, bounds that are set, a guardian that is independent.
131
+
132
+ **The covenant list is open; the services around it are a pointer.** The ten covenants and ERC-8004 are the base this program is complete over, not a ceiling: the manifest reads what a cell's world actually attaches off the world's contract keys, so an eleventh covenant — a repository under `vendor/`, contracts under a new key, a kernel module, capabilities in the catalog, a checker for the doctor — appears in `world.covenants` and `world.extensions` with no change to the program, and a world that lacks one does not claim it (docs/extending.md). The cell specification is versioned the same way: FOGA-Cell-v1's text is never edited in place, a change to what a cell promises is a second specification cells adopt. And the manifest carries, beside the replication pointer, a `services` block: a catalog URL the maintainer controls (the live list) and the offers known at build time — the free ones available, the paid ones (standing attestation, a hosted cell, dispute resolution) marked `planned`, a slot and not a sale. It obeys the propagation rules — free, material not hook, visible and declinable (`presence.services.catalog: null` drops it; an operator's own catalog replaces it) — and when a service is paid it is paid through the covenants themselves, a tender or an attestation, not a billing layer beside them. None of it depends on the reference cell: becoming a cell passes through the npm package and the chain, never through the OG; off, the OG loses only its own surface.
@@ -0,0 +1,25 @@
1
+ # The cell's documents (foga cell docs build | publish | status)
2
+
3
+ One configuration, several documents (design memo, section 2, "One local config, several documents", and section 9, 3d-2): everything a cell says about itself to the outside is generated from `cell.config.json` and the chain, each document carrying only the fields its standard prescribes, the shared metadata beside them in `manifest.json`. The node serves them; `foga cell docs publish` commits what the chain should hold and reads it back.
4
+
5
+ | File | Standard | What it says | On chain |
6
+ |---|---|---|---|
7
+ | `registration.json` | ERC-8004 `registration-v1` | name, description, image, the services (MCP `2026-07-28`, A2A `1.0`, web), `registrations` (the agent id and its registry), `x402Support: false`, `active`, `supportedTrust`, an `x-foga` block (cell, anchor, binding, specification) | `setAgentURI(agentId, uri)` on the identity registry |
8
+ | `aid-document.json` | ERC-8434 `aid-document/v1` | the identity facet (`aid:core/identity/v1`, digest of the registration), the standing facet when an issuer attested it (`aid:core/kya/v1`), the skills facet (`aid:skills/erc8338/v1`), the endpoints facet when the cell has a public URL (`aid:endpoints/v1`) | `setDocumentURI(uri, digest)` on the AID registry; digest = keccak256 of the JCS form |
9
+ | `ard-agent.json` | ERC-8444 agent descriptor | the descriptor the birth registered, with the offered skills among the capabilities (`erc8338:<name>`) and the registration | `updateDescriptor(recordId, …)` on the cell's record |
10
+ | `auf-unit.json` | ERC-8454 unit definition | the cell's economic boundary (agent element, treasury position, the ports) | `publishVersion(unitId, boundaryHash, uri, [])`: a new version when the boundary changed |
11
+ | `agent-card.json` | A2A 1.0 AgentCard | the JSON-RPC interface at the A2A URL, the offered skills by name and version, the credential schemes, no streaming or push | not on chain; served at `/.well-known/agent-card.json` (docs/a2a.md) |
12
+ | `server.json` | MCP registry manifest (2025-12-11) | the package's manifest with this cell's MCP URL as the remote | not on chain; what `mcp-publisher` publishes for the OG instance |
13
+ | `manifest.json` | `foga.documents/1` | cell, account, agent id, chain, version, built time, public URL, and per file its content hash (keccak256 of the JCS form) and size | — |
14
+
15
+ ## Where and how
16
+
17
+ `foga cell docs build --config <cfg>` writes them under `runtime.http.documentsDir` (`documents`, relative to the configuration's directory). The version counts up when any content hash changed since the last build; an unchanged build keeps its version. The node serves the directory as built: `GET /documents/<file>` for each file and the manifest, `GET /.well-known/agent-card.json` (the card) and `GET /.well-known/agent-registration.json` (the registration, where ERC-8004 domain verification looks); nothing else under those paths, and `not published yet` until a build exists.
18
+
19
+ The URI a commitment carries depends on `runtime.http.publicUrl`: with one, the registration is committed as `<publicUrl>/.well-known/agent-registration.json` and the AID document as `<publicUrl>/documents/aid-document.json`; without one, each is committed as a `data:` URI holding the document itself, so a cell with no public face is still complete on chain. The ARD descriptor and the unit definition are always committed as `data:` URIs (the publication form their standards use). The endpoints in the registration and the card are `presence.endpoints` when set, otherwise derived from the public URL (`/mcp`, `/a2a`, the URL itself); `presence.provider` names the organization behind the card.
20
+
21
+ `foga cell docs publish --config <cfg> [--wallet <dir>]` builds, then runs the capability `docs_publish` with the operator key: one keyed job (its id derived from the documents' hashes and the URI base, so the same documents are the same job and nothing is sent twice) whose steps are only the commitments that differ from what the chain holds — a document already on chain as built is skipped, and a unit whose boundary did not change gets no new version. Each step is the operator's own call through the account (`setAgentURI`, `setDocumentURI`, `updateDescriptor`, `publishVersion` are in the `identity` scope group; a cell born before `publishVersion` joined it needs the owner's `setOperatorAllowedBatch`, and `foga node preflight` says so). Afterwards every commitment is read back from the chain and compared with what was built — the registration fetched from its URL and hashed against the AID identity facet's digest, the document URI's digest, the descriptor hash, the latest unit version's boundary hash — and a mismatch is a failed publish (exit 2), as the memo's v0.2 asks. `foga cell docs status` prints the same comparison without committing anything, and `docs_status` is the public read of it over MCP and HTTP.
22
+
23
+ ## Over MCP and HTTP
24
+
25
+ `docs_status` (read, public) and `docs_publish` (act, the operator's view) are catalog capabilities like any other: an MCP client with the operator key, or an HTTP caller whose grant reaches act and operator visibility, publishes with the same checks and records. The capability's own read-back does not fetch URLs (a node answering its own request is not the check wanted); the CLI's does.
@@ -8,8 +8,8 @@ One server definition, three clients (docs/mcp.md, "Client configuration"). Each
8
8
  | `claude-code-mcp.json` | Claude Code | a project's `.mcp.json` (or `claude mcp add foga -e FOGA_OPERATOR_KEY_FILE=… -- npx -y @finchip.ai/foga mcp --config …`) |
9
9
  | `cursor-mcp.json` | Cursor | `.cursor/mcp.json` (shown without a key: reads, dry runs and builds) |
10
10
 
11
- `FOGA_OPERATOR_KEY_FILE` names a 0600 file holding the cell's operator key (`foga keys export --role operator.A`, or any key the account names as operator); with it the server acts for the cell, without it the client gets reads, dry runs and builds. `FOGA_RPC_URL` is needed only when the cell configuration names no `rpc`. From a checkout, replace `npx -y @finchip.ai/foga` with `node /path/to/foga/bin/foga.js` after `npm run build`.
11
+ `FOGA_OPERATOR_KEY_FILE` names a 0600 file holding the cell's operator key — a single-key wallet's `signer.key` (`foga wallet new`, docs/wallet.md: `~/.foga/wallet/signer.key`; a `--cell` wallet's `operator.key`), `foga keys export --role operator.A`, or any key the account names as operator; with it the server acts for the cell, without it the client gets reads, dry runs and builds. `FOGA_RPC_URL` is needed only when the cell configuration names no `rpc`. From a checkout, replace `npx -y @finchip.ai/foga` with `node /path/to/foga/bin/foga.js` after `npm run build`.
12
12
 
13
- `foga quickstart` (docs/quickstart.md) births a cell on a local chain and writes these three files, plus `http-mcp.json` for the served node (`{"url": "http://127.0.0.1:8787/mcp", "headers": {"Authorization": "Bearer <key>"}}`), with the real paths and key under `~/.foga/quickstart/mcp/`.
13
+ `foga quickstart` (docs/quickstart.md) makes the wallet on its first run, births a cell with it on a local chain and writes these three files, plus `http-mcp.json` for the served node (`{"url": "http://127.0.0.1:8787/mcp", "headers": {"Authorization": "Bearer <key>"}}`), with the real paths and key under `~/.foga/quickstart/mcp/`. A client of another cell's served node — one that issued no key — puts the header `foga wallet sign --url <that node's URL>` prints in place of the Bearer key: a session envelope signed by the wallet, good for ten minutes, at the level the cell's policy grants the address.
14
14
 
15
15
  Windows: in PowerShell the package name needs quotes on the command line — `npx -y '@finchip.ai/foga' mcp --config D:\cells\D.json` — because a bare `@` starts a splatting expression; inside these JSON files nothing is quoted differently, and paths are written with escaped backslashes (`"D:\\cells\\D.json"`). `anvil` for a local chain comes from Foundry's `win32_amd64` release zip (https://github.com/foundry-rs/foundry/releases): unzip, put the folder holding `anvil.exe` on `PATH` or set `FOGA_FOUNDRY_DIR` to it.
@@ -0,0 +1,49 @@
1
+ # Extending FOGA: the eleventh covenant, the services slot, what survives the OG
2
+
3
+ FOGA is complete over ten covenants and ERC-8004 today. Nothing in the program assumes that is the last word: the covenant list is read off the world, the kernel and the catalog are registries, the doctor judges per standard, and the manifest carries a slot for the services around the covenants that the maintainer fills without a code release. This page is the map of those seams — where an eleventh covenant goes, how the services pointer works and is declined, how the cell specification would move to a second version, and what keeps working when the reference cell is off.
4
+
5
+ ## The covenant list is data
6
+
7
+ `FOGA_COVENANTS` (`src/node/propagation.ts`) names the set this build is complete over: the base, not a ceiling. What a cell's world *actually* has is read off the world file's contract keys — `8414/TaskToken` is ERC-8414, `identity8004/…` is ERC-8004 — by `World.covenants()` (`src/core/world.ts`), and the propagation manifest reports both:
8
+
9
+ ```
10
+ "covenants": ["ERC-8004", "ERC-8338", …, "ERC-8471"], // the program's base set
11
+ "world": { "covenants": ["ERC-8004", …, "ERC-8471"], "extensions": [] }
12
+ ```
13
+
14
+ A world that attaches contracts under a new key — `9001/SomethingRegistry` — advertises `ERC-9001` under `world.covenants` and in `world.extensions` with no change to the program; a world that lacks one of the base set does not claim it. The doctor's layer 5 and the catalog's `ercs` dimension work the same way: per standard, from the world and the capabilities registered, never from a fixed count.
15
+
16
+ ## Adding a covenant, in five places
17
+
18
+ An eleventh covenant is a repository under `vendor/` and five additions, each in a registry that already exists:
19
+
20
+ 1. **The world.** Its contracts enter the world file under the ERC's number (`"9001/<Name>": {address, bundleKey}`) — `foga world deploy` from the bundle, or `foga world attach` to an instance that exists. `World.covenants()` sees it at once.
21
+ 2. **The kernel.** A module like `src/core/asf.ts` or `src/core/tenders.ts`: the ABI from the bundle, the encoders and reads the catalog and the genesis need. `Kernel` holds one per standard.
22
+ 3. **The catalog.** Capabilities in `src/tools/<covenant>.ts`, each declaring its action, visibility and principal, with the ERC in its `ercs`; registered in `src/tools/index.ts`. The CLI, MCP, HTTP and A2A entrances pick them up without a line of their own: an entrance is a view of the catalog.
23
+ 4. **The doctor.** A checker in `src/doctor/` that names its checks after the standard; layer 5 judges it beside the others (docs/doctor.md).
24
+ 5. **The manifest and the documents.** Nothing to add for the manifest (it reads the world); the ERC-8434 AID document and the ERC-8444 descriptor gain a facet or a field only if the covenant has something a counterparty must be told (docs/documents.md).
25
+
26
+ A cell born on a world with the eleventh covenant attached is complete over eleven; one born on a world without it is complete over ten, and says so.
27
+
28
+ ## The cell specification: one version, then a second
29
+
30
+ Every cell adopts the FOGA-Cell-v1 safety specification (ERC-8468): `foga cell init` registers it under the owner when the configuration names none and adopts it when the owner published it before, and the account binds to it. Its content hash is what the binding holds, so **its text is not edited in place** — an edit would make every existing cell's configuration disagree with the chain and every new birth from an owner who published before fail its check (`src/node/init.ts`). A change to what a cell promises is a *second version*: `fogaCellSpecification()` grows a sibling, a new cell names it in `identity.safety` or registers it, and an existing cell moves by presenting a binding to the new specification (`binding_present`). Two versions coexist on one world; the doctor and the manifest read which one a cell holds.
31
+
32
+ ## The services slot
33
+
34
+ The manifest carries, beside the program and the replication pointer, a `services` block (docs/propagation.md):
35
+
36
+ ```
37
+ "services": {
38
+ "catalog": "https://foga.finchip.ai/services.json",
39
+ "maintainer": "FinChip.ai",
40
+ "offers": [ { "id": "become-a-cell", "kind": "free", "status": "available", … }, { "id": "kya-attestation", "kind": "paid", "status": "planned", … }, … ],
41
+ "note": "optional and declinable; the catalog is the live list; a planned offer is not for sale yet"
42
+ }
43
+ ```
44
+
45
+ It is a pointer, deliberately: the catalog behind the URL is the live list, so what the maintainer offers — the free things, and in time the paid ones (standing attestation under the FOGA scheme, a hosted cell, dispute resolution) — changes there, with no release and no change to any cell already running. A `planned` offer is a slot, not a sale. The pointer resolves from the first day: the reference cell serves the maintainer's catalog at `/services.json` as `foga.services/1` from what its build knows (any cell whose pointer is its own URL does the same for its own list), and a static file at that path on the host, answered by the proxy ahead of the node, replaces it whenever the live list moves on (docs/propagation.md). Everything about it is visible and declinable, like the replication pointer: an operator who does not want the maintainer's catalog in their cell's manifest sets `presence.services.catalog` to `null`; one with services of their own names their catalog and offers there. The services themselves, when they are paid, are paid the way the covenants already pay for anything — a tender (ERC-8414) to a cell that offers the skill, or an attestation (ERC-8419) by an issuer — not a billing system beside the protocol.
46
+
47
+ ## What survives the OG being off
48
+
49
+ The reference cell at `foga.finchip.ai` is one running instance. Becoming a cell never passes through it: `npx -y @finchip.ai/foga quickstart` needs the npm registry and nothing else; a birth on a public chain needs the npm registry, an RPC and the world's contracts, which are on the chain. The replication pointer every cell carries names the package, not the OG. When the OG is off, what stops is the OG's own surface — its endpoints, its manifest and card, its place as a counterparty; the npm package, the GitHub repository, the MCP registry's package entry and the documentation site stay, and every pointer in a cell's documents resolves to those or to the chain. The program is the genome; the OG is the first body. docs/og-deployment.md says how it is served, and `foga cell advise` what any cell — the OG included — should do once it runs.
package/docs/loop-d.md CHANGED
@@ -106,3 +106,14 @@ foga doctor --loop-d deployments/loop-d.local.json --compare deployments/loop-d.
106
106
  ## What the records do not show, by design
107
107
 
108
108
  A refusal can name an operation that exists already (a conflict, an execution refusal), so "no operation per refusal" is read as "no new broadcast". An allowed operation can still revert on chain when state races between the simulation and the sending; that is the chain refusing, recorded as an `execution` decision, not a policy refusal. The on-chain scan looks inside `execute` (target, selector, value) rather than at the outer `to` alone, which is always the account. The budget is four books — native value out, the operator's fees (failed and replaced attempts included), the model's calls, the skill runs and commitments — and the caps in the configuration are the operator's own choice, not a guarantee that every cost has a hard ceiling: settlements and withdrawals of what was committed to still need fees after the day's cap is reached.
109
+
110
+ ## On a public chain: what the Sepolia run taught (0.6.0)
111
+
112
+ The first Loop D with an outside agent on Sepolia (2026-11-04, world `sepolia-11155111-20261101T043153Z`, cell D agent 11264, the agent a person driving the HTTP entrance with the key) passed every invariant row — every operation under an allowed decision, eleven refusals across every gate with nothing broadcast, the budget books, all operator transactions at the account and allow-listed and none reverted, the recovery drill across the kill and restart, idempotency, conflict, concurrency — and cases a, c, d, d2 and e as expected. Four things about a public chain came out of it, and 0.6.0 carries the answers:
113
+
114
+ - **A working directory is one run.** `foga loop-d run` in a directory that holds a previous run's files found that run's keyed operations and replayed them against a cell with fresh keys, and the birth failed on the first replayed answer (`the registry assigned …; expected …`). The orchestrator now clears its own files from the working directory first (`cleanLoopDDir`, the run's files and nothing else; the evidence under `--out` is kept) — or pass `--dir` for a fresh one.
115
+ - **Role funding is priced with a floor.** The roles' gas reserve was `units × gasPrice × 2`, and Sepolia quoted a gas price of a few wei: the operator got the flat 0.003 ETH alone, below the cell policy's gas floor (a quarter of `dailyGasWei`), so the budget gate refused every tender; the guardian got next to nothing for its suspend and resume. The reserve is now priced at the chain's gas price floored at 1 gwei (`src/demo/birth.ts`); a local chain, above the floor, is unchanged. A funder on Sepolia should hold about 0.15 ETH for a run.
116
+ - **The settlement figure is the tender's, not the account's.** `tender_settle` reported what it paid as the account's balance before and after; between those two reads the orchestrator's concurrent mints escrowed value out of the same account, and the evidence recorded a reward the chain paid in full (0.001 ETH, confirmed on chain) as 0.0002. It now reads the escrow the settlement released less what it left as credit, on the tender itself (`src/tools/tenders.ts`); nothing else on the account leaks in, and a rejected settlement reads as paying nothing.
117
+ - **A log scan is asked in windows.** `foga-probe` over a tender's lookback (9000 blocks) was one `eth_getLogs` per section; a shared endpoint answered it slowly or not at all and the worker's minute ran out (`refused at execution`, correctly recorded — and case f was left `NotTested` for it). The probe now asks a bounded scan in windows of 2000 blocks (`src/genesis/probe.ts`; the same logs in the same order, so the answer hash is unchanged), and the worker's budget on a public chain is three minutes (`skills.timeoutMs` overrides it).
118
+
119
+ Two practical notes for the operator of such a run: `cast send` with the chain's near-zero gas price was dropped by the endpoint (`--gas-price 3gwei` lands); and the orchestrator's phase ends after three idle minutes once the agent has acted, so an agent that pauses — to fund something, to read — should expect the run to have moved on. The free public endpoints also log `could not coalesce error … block range extends beyond current head block`: a load-balanced cluster whose nodes lag each other by a block; harmless. `foga cell advise` says this and more to any cell on a public chain.
package/docs/mcp.md CHANGED
@@ -7,7 +7,7 @@
7
7
  | Transport | Started by | Who uses it | Who the caller is |
8
8
  |---|---|---|---|
9
9
  | stdio | `foga node mcp --config <cell.config.json>` (`foga mcp --config …` is the same command; `npx -y @finchip.ai/foga mcp --config …` from the npm package) | a local client on the same machine: Claude Desktop, Claude Code, Cursor | with `FOGA_OPERATOR_KEY[_FILE]` in the environment, the **operator** (every level and the operator's data); without a key, the **local user**: build level (reads, dry runs, builds) with the operator's view of the store |
10
- | Streamable HTTP | `foga node serve --config <cell.config.json> [--listen host:port]`, path `/mcp` | remote clients, the public endpoint of the OG instance (3d) | without `Authorization`, the **public**: build level, public data; with `Authorization: Bearer <key>`, the grant the key names (`access.keys`, below); an unknown key is 401 |
10
+ | Streamable HTTP | `foga node serve --config <cell.config.json> [--listen host:port]`, path `/mcp` | remote clients, the public endpoint of the OG instance (3d) | without `Authorization`, the **public**: build level, public data; with `Authorization: Bearer <key>`, the grant the key names (`access.keys`, below); with `Authorization: FOGA-EIP712 <envelope>`, a **signer** — the wallet that signed, at the level `access.signers` grants it (docs/wallet.md); an unknown key or a signature that does not verify is 401 |
11
11
 
12
12
  The stdio server's stdout carries protocol messages alone; everything it says goes to stderr. A node process runs in a **node environment** (docs/node.md, "The watchers"): the operator key alone or no key at all, and an environment that also holds a phrase, a deployer key, an owner's or a guardian's key is refused before anything is read. Admin capabilities (`operator_rotate`, `guardian_suspend`, `guardian_resume`, `binding_present`, `identity_transfer`) are not offered over MCP or HTTP at all in Phase 3: they are the owner's and the guardian's CLI commands, run once with the key loaded for that command.
13
13
 
@@ -31,7 +31,7 @@ The tool list is the catalog filtered by the caller (`catalog.list(caller)`): a
31
31
  | `<name>_simulate` | `simulate` | not read-only (a decision record is written), not destructive | the caller's level is `build` or above |
32
32
  | `<name>_build` | `build` | `readOnlyHint: true`, `openWorldHint: false` (calldata for the caller's own account, `as`) | level `build` or above; not for `tender_precheck` (nothing to sign) or `skill_run` (nothing on chain) |
33
33
 
34
- So the public gets 11 tools (the ten public reads and `tender_precheck_simulate`), a local keyless user 31 (the 15 reads and every dry run and build), the holder of an `act`/`caller` key 36, the operator 40; the catalog has 29 capabilities. Input and output schemas are the capability's zod schemas published as JSON Schema 2020-12.
34
+ So the public gets 13 tools (the twelve public reads and `tender_precheck_simulate`), a local keyless user 35 (the 17 reads and every dry run and build), the holder of an `act`/`caller` key 38, the operator 45; the catalog has 32 capabilities. Input and output schemas are the capability's zod schemas published as JSON Schema 2020-12.
35
35
 
36
36
  Results are `structuredContent` under the tool's output schema (and the same JSON as text):
37
37
 
@@ -41,7 +41,7 @@ Results are `structuredContent` under the tool's output schema (and the same JSO
41
41
  - **`tender_precheck` reports an ineligible tender as an ordinary result**, `{eligible: false, gate, reason, decisionId}`: finding out is what it is for. An eligible one is `{eligible: true, decisionId, jobId, result}`.
42
42
  - Bad input is a tool error (`isError`, gate `input`); a tool that is not offered to the caller is an unknown tool to the SDK.
43
43
 
44
- Resources: `foga://world` (world id, chain, contracts, bundle hash), `foga://cell/status`, `foga://policy` (the budget and tender rules applied to every act), `foga://spec/foga-cell-v1` (the ERC-8468 specification every cell adopts), `foga://docs/compatibility-matrix` (text/markdown). One prompt, `take-tender` (`tokenId`): work one tender the way the operator would — inspect, pre-check, run the skill, submit, settle — and stop at a refusal rather than look for a way around it. `spec_read` is the same material as a tool, for clients that read tools only.
44
+ Resources: `foga://world` (world id, chain, contracts, bundle hash), `foga://cell/status`, `foga://policy` (the budget and tender rules applied to every act), `foga://spec/foga-cell-v1` (the ERC-8468 specification every cell adopts), `foga://docs/compatibility-matrix` and `foga://docs/quickstart` (text/markdown: the matrix, and how to become a cell oneself). Three prompts: `take-tender` (`tokenId`) — work one tender the way the operator would: inspect, pre-check, run the skill, submit, settle, and stop at a refusal rather than look for a way around it; `become-a-cell` — the genome's instructions to an agent that met this cell: make a wallet, run `foga quickstart`, connect, then `foga cell new --wallet` and `foga cell init --wallet` on a public chain (docs/wallet.md, "become-a-cell"); and `spread-the-cell` — the optional, zero-cost, on-its-own-cadence prompt to help FOGA propagate by consent: fetch `propagation_kit`, offer the declinable pointer where it fits, stay discoverable for free, and spend gas on on-chain discoverability only if it chooses (docs/propagation.md). `spec_read` is the same material as a tool, for clients that read tools only.
45
45
 
46
46
  ## The HTTP API
47
47
 
@@ -51,14 +51,17 @@ Same port, same catalog (`node:http`, no framework; the dependency tree stays et
51
51
  |---|---|
52
52
  | `POST /mcp` (and `GET`/`DELETE` as the protocol era defines them) | MCP over Streamable HTTP |
53
53
  | `GET /healthz` | `{ok, cell, account, worldId, chainId, block, version}`; 503 when the chain does not answer |
54
- | `GET /` | the version, `cell_status` as the caller sees it, the number of decision records, the endpoints |
55
- | `GET /api/capabilities` | the catalog as this caller sees it (name, action, visibility, principal, modes, schemas) |
54
+ | `GET /` | the version, `cell_status` as the caller sees it, the number of decision records, the endpoints (`mcp`, `api`, `a2a`, `agentCard`, `documents`) |
55
+ | `GET /api/capabilities` | the catalog as this caller sees it (name, action, visibility, principal, modes, schemas), and the caller itself (`id`, `level`, `visibility`, `via`: `anonymous`, `key` or `signature`) |
56
56
  | `POST /api/<capability>` `[?mode=simulate\|build\|act]` | the capability with the JSON body as input (`application/json`); `act` is the default mode |
57
57
  | `GET /api/<read capability>?<field>=<value>` | a read with query parameters as input |
58
58
  | `GET /api/jobs/<jobId>`, `GET /api/operations/<operationId>`, `GET /api/decisions[?capability=&verdict=&limit=]` | `job_status`, `operation_status`, `decision_list` |
59
- | `/.well-known/*`, `/documents/*` | 404 `not published yet` (3d) |
59
+ | `POST /a2a` | the A2A 1.0 JSON-RPC binding (docs/a2a.md); `A2A-Version: 1.0` |
60
+ | `GET /.well-known/foga-cell.json` | the propagation manifest (docs/propagation.md): free, built live from the configuration, answered even before any document is published |
61
+ | `GET /services.json` | the services catalog (`foga.services/1`), served only by a cell whose manifest's services pointer is its own URL — the reference cell for the maintainer's list; 404 with the reason otherwise (docs/propagation.md) |
62
+ | `GET /.well-known/agent-card.json`, `GET /.well-known/agent-registration.json`, `GET /documents/<file>` | the cell's documents as `foga cell docs build` wrote them (docs/documents.md); 404 `not published yet` before a build |
60
63
 
61
- Status codes say what kind of refusal it is: **401** the Authorization header carries no key the node knows; **403** a permission refusal (the capability is not the caller's, the mode is above the caller's level, the Host or Origin is not allowed); **400** invalid input or an unknown mode; **404** an unknown capability or path; **405** an act asked with GET; **413** a body over `maxBodyBytes`; **415** a body that is not `application/json`; **429** more than `rateLimitPerMinute` requests from one address. **A business refusal is 200 with `{ok: false, gate, reason, decisionId}`** — the catalog's answer, as the CLI prints it — and an allowed act is 200 with `{ok: true, output, decisionId, jobId, operations}`.
64
+ Status codes say what kind of refusal it is: **401** the Authorization header carries no key the node knows, a signature that does not verify (expired, replayed, another cell's domain) or a scheme the node does not accept; **403** a permission refusal (the capability is not the caller's, the mode is above the caller's level, the Host or Origin is not allowed, a signature where the policy accepts none); **400** invalid input or an unknown mode; **404** an unknown capability or path; **405** an act asked with GET; **413** a body over `maxBodyBytes`; **415** a body that is not `application/json`; **429** more than `rateLimitPerMinute` requests from one address. **A business refusal is 200 with `{ok: false, gate, reason, decisionId}`** — the catalog's answer, as the CLI prints it — and an allowed act is 200 with `{ok: true, output, decisionId, jobId, operations}`.
62
65
 
63
66
  ## API keys
64
67
 
@@ -66,6 +69,10 @@ Status codes say what kind of refusal it is: **401** the Authorization header ca
66
69
 
67
70
  Keys are for the operator's own integrations and named partners; the public endpoint reads and builds without one. OAuth is not in Phase 3 (design memo, section 4).
68
71
 
72
+ ## Signed callers
73
+
74
+ The third credential needs no issuer: `Authorization: FOGA-EIP712 <base64url envelope>`, an EIP-712 signature by the caller's own wallet in this cell's domain, either bound to the one request (method, path, body hash; 120 s, good once) or a session (600 s, reusable — for a client that holds one static header). The caller is the signing address (`{id: <address>, entrance: "http", level, visibility}`, its decisions recorded under it), and the level is what `access.signers` grants: the cell's own operator or owner signing remotely, an allow-list by address or ERC-8004 agent id, a KYA standing threshold, or the default (`build`, `read`, or `none` to refuse signatures); an act wants a request-bound envelope unless the policy says otherwise. `foga wallet sign --url <the node's URL>` prints a header; docs/wallet.md has the envelope, the types and the policy. Over stdio there is nothing to sign.
75
+
69
76
  ## The edge
70
77
 
71
78
  - `runtime.http.listen` defaults to `127.0.0.1:8787`: the server binds the loopback and a reverse proxy (Caddy on the OG host, 3d) terminates TLS in front of it. On a loopback listener the `Host` header must be a local one unless `allowedHosts` names the public host names the proxy forwards; an `Origin` header, when a browser sends one, must be local or in `allowedOrigins` (no CORS headers are ever sent — this is the Origin check the protocol asks for, not a cross-origin grant); `X-Forwarded-For` is trusted only when the socket is the loopback.
@@ -81,7 +88,7 @@ foga node serve --config cell.config.json # no key: reads, dry r
81
88
  foga node serve --config cell.config.json --no-watch # the server without the watchers
82
89
  ```
83
90
 
84
- With the operator key and a born cell, the watchers (heartbeat, tender discovery, settlement, safety) run beside the server on `runtime.pollSeconds`; their lines go to stderr. `SIGINT`/`SIGTERM` close the server and the store. `foga node run` is the same server with the runner beside the watchers — a model proposing acts the catalog judges (docs/loop-d.md). The systemd unit and the Caddy site of the OG host are part of 3d.
91
+ With the operator key and a born cell, the watchers (heartbeat, tender discovery, settlement, safety) run beside the server on `runtime.pollSeconds`; their lines go to stderr. `SIGINT`/`SIGTERM` close the server and the store. This is **host mode**, the OG instance's default: the cell is reachable on every entrance, keeps its commitments, and runs no model. `foga node run` is the same server with the runner beside the watchers — a model proposing acts the catalog judges (**embedded mode**, on demand; docs/loop-d.md). `--wallet <dir>` on either takes the operator key from a wallet instead of the environment (docs/wallet.md); `foga node preflight` before the first serve says what is missing (docs/node.md, "Preflight"). The systemd unit, the Caddy site and the `/etc/foga/og.env` of the OG host are in `deploy/`, and docs/og-deployment.md is the runbook.
85
92
 
86
93
  ## Client configuration
87
94
 
@@ -92,14 +99,14 @@ With the operator key and a born cell, the watchers (heartbeat, tender discovery
92
99
  `claude mcp add foga -e FOGA_OPERATOR_KEY_FILE=/home/me/.foga/keys/operator.key -- npx -y @finchip.ai/foga mcp --config /home/me/cells/D.json`
93
100
  - Cursor — `docs/examples/cursor-mcp.json` (`.cursor/mcp.json`)
94
101
 
95
- Each runs `npx -y @finchip.ai/foga mcp --config <cell.config.json>` with, optionally, `FOGA_OPERATOR_KEY_FILE` (the operator acts) and `FOGA_RPC_URL` (when the config names no RPC) in `env`. From a checkout the command is `node` with `/path/to/foga/bin/foga.js mcp --config …` after `npm run build`. Without a key file the client gets reads, dry runs and builds: enough to inspect tenders, pre-check them and prepare calldata; nothing it says can make the cell act. A served node is added as a remote: Claude Code with `claude mcp add --transport http foga http://127.0.0.1:8787/mcp --header "Authorization: Bearer <key>"`, other clients with `{"url": "…/mcp", "headers": {"Authorization": "Bearer <key>"}}`; the public OG endpoint `https://foga.finchip.ai/mcp` goes live in 3d. In PowerShell the package name needs quotes on the command line (`npx -y '@finchip.ai/foga' …`: a bare `@` starts a splatting expression); inside a JSON file nothing changes.
102
+ Each runs `npx -y @finchip.ai/foga mcp --config <cell.config.json>` with, optionally, `FOGA_OPERATOR_KEY_FILE` (the operator acts) and `FOGA_RPC_URL` (when the config names no RPC) in `env`. From a checkout the command is `node` with `/path/to/foga/bin/foga.js mcp --config …` after `npm run build`. Without a key file the client gets reads, dry runs and builds: enough to inspect tenders, pre-check them and prepare calldata; nothing it says can make the cell act. A served node is added as a remote: Claude Code with `claude mcp add --transport http foga http://127.0.0.1:8787/mcp --header "Authorization: Bearer <key>"`, other clients with `{"url": "…/mcp", "headers": {"Authorization": "Bearer <key>"}}` — or, for an agent with a wallet and no key from this cell, the header `foga wallet sign --url <the node's URL>` prints (a session envelope: reads, dry runs and builds as itself, and whatever the cell's policy grants its address); the public OG endpoint `https://foga.finchip.ai/mcp` goes live in 3d-3. In PowerShell the package name needs quotes on the command line (`npx -y '@finchip.ai/foga' …`: a bare `@` starts a splatting expression); inside a JSON file nothing changes.
96
103
 
97
104
  ## The package
98
105
 
99
- The npm package is `@finchip.ai/foga` — npm refuses the unscoped name `foga` as too similar to `ora` and `koa`, so it lives under the `finchip.ai` scope; the binary is `foga` and `foga mcp` is the stdio server. `package.json` carries `mcpName: "io.github.garyyang-finchip/foga"`, the name the MCP registry verifies npm ownership by, and `server.json` at the repository root is the registry manifest: the npm package as a stdio server (`npx -y @finchip.ai/foga node mcp --config …`, `FOGA_OPERATOR_KEY_FILE` and `FOGA_RPC_URL` as its environment) and `https://foga.finchip.ai/mcp` as the remote. `engines.node` is `>=22.13`, where `node:sqlite` runs without a flag; CI runs Node 22. Publication to npm and to the registry is part of 3d (the memo's steps: `npm publish` by the package's owner, then `mcp-publisher login github` and `mcp-publisher publish`); the manifest's remote must be reachable when it is published.
106
+ The npm package is `@finchip.ai/foga` — npm refuses the unscoped name `foga` as too similar to `ora` and `koa`, so it lives under the `finchip.ai` scope; the binary is `foga` and `foga mcp` is the stdio server. `package.json` carries `mcpName: "io.github.garyyang-finchip/foga"`, the name the MCP registry verifies npm ownership by, and `server.json` at the repository root is the registry manifest: the npm package as a stdio server (`npx -y @finchip.ai/foga node mcp --config …`, `FOGA_OPERATOR_KEY_FILE` and `FOGA_RPC_URL` as its environment) and `https://foga.finchip.ai/mcp` as the remote; `foga cell docs build` writes a cell's own copy with its MCP URL as the remote (docs/documents.md). `engines.node` is `>=22.13`, where `node:sqlite` runs without a flag; CI runs Node 22. The package is published on npm by its owner (`npm publish --tag latest` from a checkout at the tagged version; `npm view @finchip.ai/foga version` shows what is live); publication to the registry (`mcp-publisher login github`, then `mcp-publisher publish` with the OG cell's `server.json`) was done in 3d-3, once the manifest's remote answered: the OG cell is listed as `io.github.garyyang-finchip/foga`, and a re-publish follows each change to the manifest (docs/og-deployment.md, "The MCP registry listing").
100
107
 
101
108
  What ships: `bin/`, `dist/`, `artifacts/bundle.json` (the ABIs), `worlds/`, `docs/` (the rule texts `spec_read` serves), `scenarios/` (the skill packages and the fixture), the prover bridge and the circuit's verification key, `server.json`. A checkout installed straight from git (`npm install github:garyyang-finchip/foga`, `npx -y github:garyyang-finchip/foga …`) compiles itself on install (`prepare`, `scripts/prepare.mjs`: only when `dist/` is missing and `src/` is there); `.gitattributes` keeps every text file LF on every platform, the launcher's shebang line included. `scripts/pack-smoke.mjs <rpc> <world.json>` is the pre-publication check and a CI step: `npm pack`, a clean `npm install` of the tarball in an empty directory, then the packaged CLI alone — `--help`, the public catalog, a schema, a cell born with `foga cell new` and `foga cell init` on the given world, and `foga node mcp` without a key driven by a raw 2025-era JSON-RPC client over stdio (stdout protocol-only, the keyless tool set, `cell_status`, `resources/list`, `spec_read` of the compatibility matrix from inside the package, no key-shaped value on stderr).
102
109
 
103
110
  ## Acceptance
104
111
 
105
- `test/mcp.test.ts` (anvil; the 3b gate of the design memo, section 11): a cell born through the CLI (`test/helpers/anvil-cell.ts`), two tenders minted by a demander (a to take, c for a skill the cell lacks), an API key minted by `foga node key new` and checked in the config as its hash alone. Over Streamable HTTP, in process: the health and front pages; the edge (401 for a bad, a foreign-id and a non-Bearer credential, 403 for a foreign Origin and Host, 413, 415, 400, 404, 405); the JSON API as the public (reads and dry runs, 403 for an act and for a caller-visible capability, a business refusal as 200 `ok: false`); a client pinned to 2026-07-28 as the public (the 11 tools and their annotations, `cell_status`, `tender_precheck_simulate` on c → `eligible: false` at `skill` and on a → eligible, no act offered, resources, the prompt); the key holder walking case a — `tender_precheck`, `skill_run` reproducing the demander's answer, `tender_submit`, the same job answered once, another request under the same job refused at `conflict`, `tender_settle` paying the reward to the account, `credit_withdraw` when credited — then reading its own job, operation and decision records (and none of the public's), and a `tender_mint_build` for a third party's account; a legacy client and an auto-negotiating client listing the same tools; the rate limiter on a second server. Over stdio: `foga node mcp` without a key (31 tools, `evidence_export` with every caller's records, no act) and with the operator key (every act, no admin), and a mixed environment refused before anything is read.
112
+ `test/mcp.test.ts` (anvil; the 3b gate of the design memo, section 11): a cell born through the CLI (`test/helpers/anvil-cell.ts`), two tenders minted by a demander (a to take, c for a skill the cell lacks), an API key minted by `foga node key new` and checked in the config as its hash alone. Over Streamable HTTP, in process: the health and front pages; the edge (401 for a bad, a foreign-id and a non-Bearer credential, 403 for a foreign Origin and Host, 413, 415, 400, 404, 405); the JSON API as the public (reads and dry runs, 403 for an act and for a caller-visible capability, a business refusal as 200 `ok: false`); a client pinned to 2026-07-28 as the public (the 13 tools and their annotations, `cell_status`, `tender_precheck_simulate` on c → `eligible: false` at `skill` and on a → eligible, no act offered, the resources, the three prompts); the key holder walking case a — `tender_precheck`, `skill_run` reproducing the demander's answer, `tender_submit`, the same job answered once, another request under the same job refused at `conflict`, `tender_settle` paying the reward to the account, `credit_withdraw` when credited — then reading its own job, operation and decision records (and none of the public's), and a `tender_mint_build` for a third party's account; a legacy client and an auto-negotiating client listing the same tools; the rate limiter on a second server. Over stdio: `foga node mcp` without a key (35 tools, `evidence_export` with every caller's records, no act) and with the operator key (every act, no admin), and a mixed environment refused before anything is read. `test/signed.test.ts` covers the third credential (docs/wallet.md), `test/a2a.test.ts` the fourth entrance (docs/a2a.md).
package/docs/node.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # The node (foga cell, foga node)
2
2
 
3
- `src/tools/` and `src/node/` turn the kernel into something other agents can call (design memo, Phase 3, sections 2, 3 and 5). The kernel is unchanged in kind: every chain write is still a keyed, bound, exactly-once operation (docs/kernel.md). What the node adds is a **capability catalog** — the one place authorization happens — a **policy engine** that applies the cell's own rules, **decision records** for every attempt to act, and the machinery of a cell that lives outside the genesis: a configuration file, a birth, a migration, watchers. The CLI (`foga cell …`, `foga node …`) is the first entrance; MCP over stdio and Streamable HTTP and the HTTP API (3b, docs/mcp.md) are the second and third, A2A (3d) the fourth — further entrances onto the same catalog that add no rule of their own.
3
+ `src/tools/` and `src/node/` turn the kernel into something other agents can call (design memo, Phase 3, sections 2, 3 and 5). The kernel is unchanged in kind: every chain write is still a keyed, bound, exactly-once operation (docs/kernel.md). What the node adds is a **capability catalog** — the one place authorization happens — a **policy engine** that applies the cell's own rules, **decision records** for every attempt to act, and the machinery of a cell that lives outside the genesis: a configuration file, a birth, a migration, watchers. The CLI (`foga cell …`, `foga node …`) is the first entrance; MCP over stdio and Streamable HTTP and the HTTP API (3b, docs/mcp.md) are the second and third, A2A (3d-2, docs/a2a.md) the fourth — further entrances onto the same catalog that add no rule of their own.
4
4
 
5
5
  ## One configuration: `cell.config.json`
6
6
 
@@ -9,13 +9,13 @@ A node maintains one file, schema `foga.cell-config/1` (`src/node/config.ts`, va
9
9
  | Block | Holds | Written by |
10
10
  |---|---|---|
11
11
  | `identity` | the world file, the account and its roles (owner, operator, guardian, recovery), the ERC-8004 agent id, the binding, the ARD record and AUF unit ids, the document digest, the ASF specification (`safety`), the KYA scheme and issuers the cell cites (`standing`) | the operator (roles, world) and `foga cell init` / `migrate` (everything the chain decided) |
12
- | `presence` | name, description, image, homepage, the endpoints (MCP, A2A, web) | the operator |
13
- | `skills` | the packages directory, the input directory a run may read documents from, the skills held (token, record, unit, package hash) and the ones offered | `skill_mint` |
12
+ | `presence` | name, description, image, homepage, the endpoints (MCP, A2A, web; derived from the public URL when absent), the `provider` the agent card names (organization, url) | the operator |
13
+ | `skills` | the packages directory, the input directory a run may read documents from, the skills held (token, record, unit, package hash) and the ones offered; `timeoutMs`, the skill worker's time budget per run (absent: a minute on a local chain, three on a public one, where a log scan is answered in seconds) | `skill_mint` |
14
14
  | `policy` | the budget (daily value, per-operation value, daily fees, concurrent commitments) and the tender policy (reward floor, deadline minima, acceptance authorities, counterparty level, discovery lookback) | the operator |
15
- | `runtime` | heartbeat cadence, poll interval, the HTTP server (`http`: listen address, allowed hosts and origins, public URL, rate limit, body cap), the runner's model (`model`: provider `stub` or `anthropic`, the model name, the day's call allowance, the tool calls and acts a tick may make; docs/loop-d.md) | the operator |
16
- | `access` | the API keys the HTTP entrance accepts, each as its id, the SHA-256 of the key, a level and a visibility (docs/mcp.md) | `foga node key new` |
15
+ | `runtime` | heartbeat cadence, poll interval, the HTTP server (`http`: listen address, allowed hosts and origins, public URL, rate limit, body cap, `documentsDir` — where the cell's documents are built and served from, `documents` by default; docs/documents.md), the runner's model (`model`: provider `stub` or `anthropic`, the model name, the day's call allowance, the tool calls and acts a tick may make; docs/loop-d.md) | the operator |
16
+ | `access` | the API keys the HTTP entrance accepts (`keys`: each as its id, the SHA-256 of the key, a level and a visibility; docs/mcp.md) and what a wallet signature is granted (`signers`: a default level, an allow-list by address or ERC-8004 agent id, a KYA standing threshold, whether an act needs a request-bound envelope; docs/wallet.md) | `foga node key new`; the operator |
17
17
 
18
- `foga cell new` writes a starting config from the three role addresses and the world; `foga cell init` fills in what the birth decides. Amounts are decimal strings in wei. The store named in the config is the cell's own (operations, jobs, decisions, facts) and records the account it belongs to; a config pointed at another cell's store is refused.
18
+ `foga cell new` writes a starting config from the three role addresses and the world (`--wallet <dir>` takes them from a wallet, docs/wallet.md); `foga cell init` fills in what the birth decides. Amounts are decimal strings in wei. The store named in the config is the cell's own (operations, jobs, decisions, facts) and records the account it belongs to; a config pointed at another cell's store is refused.
19
19
 
20
20
  ## The catalog and its three dimensions
21
21
 
@@ -25,7 +25,7 @@ A capability (`src/tools/types.ts`) is a name, a title and description, a zod in
25
25
  - **visibility**: `public` (anyone), `caller` (a caller sees its own jobs, operations and decisions; the operator every one), `operator` (the node's operator alone: the full evidence, the raw store, minting). A capability above the caller's scope is not listed and, when asked for, refused as if it did not exist. An operation id or a job id is a locator, never a pass: `operation_status` answers a caller only about its own.
26
26
  - **principal**: whose act it is — `none` for a read, `cell` for the node's own cell, `as` for a build made for the account the caller names (`as`), so a third party gets calldata it can execute itself rather than a call only the OG operator could make.
27
27
 
28
- The 29 capabilities of 3a and 3b (the Loop D path of the design memo's 43): reads `world_describe`, `identity_resolve`, `tender_list`, `tender_inspect`, `skill_list`, `skill_inspect`, `cell_status`, `operation_status`, `job_status`, `operation_list`, `decision_list`, `evidence_export`, `kya_check`, `safety_status`, `spec_read` (the FOGA-Cell-v1 specification, the cell's policy, the compatibility matrix and the configuration schema, each shipped in the package); acts `tender_precheck`, `skill_run`, `tender_submit`, `tender_settle`, `credit_withdraw`, `tender_mint`, `tender_fund`, `skill_mint`, `aid_heartbeat`; admin `operator_rotate`, `guardian_suspend`, `guardian_resume`, `binding_present`, `identity_transfer`. `foga cell list` prints them with their dimensions and the modes the caller may use; `foga cell schema <name>` prints the schemas.
28
+ The 32 capabilities of 3a to 3d (the Loop D path of the design memo's 43): reads `world_describe`, `identity_resolve`, `tender_list`, `tender_inspect`, `skill_list`, `skill_inspect`, `cell_status`, `operation_status`, `job_status`, `operation_list`, `decision_list`, `evidence_export`, `kya_check`, `safety_status`, `spec_read` (the FOGA-Cell-v1 specification, the cell's policy, the compatibility matrix and the configuration schema, each shipped in the package), `docs_status` (what the cell's documents say and what the chain holds of them), `propagation_kit` (the free, off-chain material for spreading FOGA — a declinable replication pointer and the cell's discovery coordinates; docs/propagation.md); acts `tender_precheck`, `skill_run`, `tender_submit`, `tender_settle`, `credit_withdraw`, `tender_mint`, `tender_fund`, `skill_mint`, `aid_heartbeat`, `docs_publish` (the documents' commitments, docs/documents.md); admin `operator_rotate`, `guardian_suspend`, `guardian_resume`, `binding_present`, `identity_transfer`. `foga cell list` prints them with their dimensions and the modes the caller may use; `foga cell schema <name>` prints the schemas.
29
29
 
30
30
  ## The three checks
31
31
 
@@ -47,7 +47,7 @@ A **job** is the unit of business a caller asks for (a tender taken, a skill run
47
47
 
48
48
  ## Birth and migration
49
49
 
50
- `foga cell init --config <cfg>` is T1 for one cell, the owner's act (the owner key from `FOGA_OWNER_KEY` or `FOGA_OWNER_KEY_FILE`, loaded for the one command): publish the FOGA-Cell-v1 specification when the config names none (the registry derives the specification's id from the publisher and a salt, so an owner who published it before — for an earlier cell of theirs — adopts that specification rather than registering it again), deploy a revision-2 `FogaCellAccount` naming the operator and guardian, fund it (`--capital-wei`, default 0.05 ETH on anvil and 0.005 ETH elsewhere), set the operator with the policy's daily value as the on-chain daily cap, open the operator's allow-list from the standalone scope groups (identity, standing, skills-provider, labour, demander, service-provider; `src/node/init.ts`, `NODE_SCOPE_GROUPS`), set the AID registry and the commitment and de-risk classes, adopt the binding, and — through the account, as owner — register the ERC-8004 identity with the presence block as registration file, bind and heartbeat the anchor (and bind it in the backing registry where the world has one), publish the AID document, the ARD agent record and the AUF unit. Every step is a keyed operation in the cell's store, so an interrupted init resumes and a repeated one replays without sending. Standing is not the cell's to give itself: an issuer attests it afterwards (`kya_check` reads it). The config is written back as the chain decides (account, agent id, binding, record, unit, document digest, specification).
50
+ `foga cell init --config <cfg>` is T1 for one cell, the owner's act (the owner key from `FOGA_OWNER_KEY` or `FOGA_OWNER_KEY_FILE`, or `--wallet <dir>` for a wallet's `owner.key`, loaded for the one command): publish the FOGA-Cell-v1 specification when the config names none (the registry derives the specification's id from the publisher and a salt, so an owner who published it before — for an earlier cell of theirs — adopts that specification rather than registering it again), deploy a revision-2 `FogaCellAccount` naming the operator and guardian, fund it (`--capital-wei`, default 0.05 ETH on anvil and 0.005 ETH elsewhere), set the operator with the policy's daily value as the on-chain daily cap, open the operator's allow-list from the standalone scope groups (identity — which since 3d-2 includes the unit registry's `publishVersion`, for `docs_publish` — standing, skills-provider, labour, demander, service-provider; `src/node/init.ts`, `NODE_SCOPE_GROUPS`), set the AID registry and the commitment and de-risk classes, adopt the binding, and — through the account, as owner — register the ERC-8004 identity with the presence block as registration file, bind and heartbeat the anchor (and bind it in the backing registry where the world has one), publish the AID document, the ARD agent record and the AUF unit. Every step is a keyed operation in the cell's store, so an interrupted init resumes and a repeated one replays without sending. Standing is not the cell's to give itself: an issuer attests it afterwards (`kya_check` reads it). The config is written back as the chain decides (account, agent id, binding, record, unit, document digest, specification).
51
51
 
52
52
  `foga cell migrate --config <old> --to <new.config.json> [--operator <addr>]` is T9 for one cell: a new revision-2 account (a fresh operator key when given — the point of a migration after a compromise), configured as at birth, with its own binding; the ERC-8004 agent moves through the old account as the owner's act; the new anchor binds the agent in every registry and the old one retires naming its successor; a new record, document and unit; each held skill token moves and its record is recommitted by the new controller. The old account's native balance is swept to the owner first (`--no-sweep` keeps it). The new config and a fresh store are written; the old cell's store holds the migration's operations and the successor's address as a fact. This is the path the OG instance takes from its revision-1 body (3d).
53
53
 
@@ -57,10 +57,34 @@ A **job** is the unit of business a caller asks for (a tender taken, a skill run
57
57
 
58
58
  ## The CLI
59
59
 
60
- `foga cell <capability> --config <cfg> [--mode simulate|build|act] [--<field> <value> …] [--json '<input>']` is generated from the catalog: the input is the capability's schema (flags for top-level fields, `--json` for the whole object), the output is printed as JSON, a refusal is `{ok: false, gate, reason, decisionId}` with exit code 2. The local CLI calls as the operator (every level, every scope); `--caller <id> --level read|build|act` calls as another caller would through an entrance, which is how the test and a curious operator see what the public or an API-key holder gets. Keys are loaded for the one command from the environment according to what the command needs: an act loads the operator's, an admin capability the owner's or the guardian's (`foga cell owner …`, `foga cell guardian …` say it explicitly), reads and dry runs none.
60
+ `foga cell <capability> --config <cfg> [--mode simulate|build|act] [--<field> <value> …] [--json '<input>']` is generated from the catalog: the input is the capability's schema (flags for top-level fields, `--json` for the whole object), the output is printed as JSON, a refusal is `{ok: false, gate, reason, decisionId}` with exit code 2. The local CLI calls as the operator (every level, every scope); `--caller <id> --level read|build|act` calls as another caller would through an entrance, which is how the test and a curious operator see what the public or an API-key holder gets. Keys are loaded for the one command from the environment according to what the command needs: an act loads the operator's, an admin capability the owner's or the guardian's (`foga cell owner …`, `foga cell guardian …` say it explicitly), reads and dry runs none. `--wallet <dir>` on any of them takes the role's key from a wallet's files instead of the environment (docs/wallet.md), and refuses to run beside `FOGA_OPERATOR_KEY[_FILE]`: a key comes from one place.
61
61
 
62
- `foga node mcp --config <cfg>` serves the catalog to a local MCP client over stdio, `foga node serve --config <cfg> [--listen host:port] [--no-watch]` serves the HTTP API and MCP over Streamable HTTP with the watchers beside them, and `foga node key new --config <cfg> --id <id> [--level] [--visibility] [--note]` mints an API key for the HTTP entrance; docs/mcp.md has the entrances, their callers and their edge. `foga node run --config <cfg> [--model stub|anthropic]` is the embedded node — the server, the watchers and the runner, a model proposing acts the catalog judges (docs/loop-d.md).
62
+ `foga node mcp --config <cfg>` serves the catalog to a local MCP client over stdio, `foga node serve --config <cfg> [--listen host:port] [--no-watch]` serves the HTTP API, MCP over Streamable HTTP, the A2A binding and the cell's documents with the watchers beside them (**host mode**, the default of an OG instance: no model, nothing spent on one, the cell answers what it is asked and the watchers keep its commitments), and `foga node key new --config <cfg> --id <id> [--level] [--visibility] [--note]` mints an API key for the HTTP entrance; docs/mcp.md has the entrances, their callers and their edge. `foga node run --config <cfg> [--model stub|anthropic]` is the embedded node — the server, the watchers and the runner, a model proposing acts the catalog judges (**embedded mode**, on demand; docs/loop-d.md). `foga cell docs build | publish | status --config <cfg>` are the cell's documents (docs/documents.md).
63
+
64
+ ## Preflight
65
+
66
+ `foga node preflight --config <cfg> [--wallet <dir>] [--rpc <url>] [--no-executors] [--no-network] [--out <report.json>] [--strict]` is the readiness reconciliation of a cell that is about to be served or run: it reads and runs, and sends nothing. Rows by area, each `Pass`, `Warn`, `Fail` or `NotTested` with its detail:
67
+
68
+ | Area | Checks |
69
+ |---|---|
70
+ | `world` | the RPC serves the world's chain; the store belongs to this world and this account |
71
+ | `account` | the cell is born; a revision-2 account; the account names the configuration's owner and operator; anchor ACTIVE and binding Satisfied (STALE is a warning — the next heartbeat restores it); the operator allow-list covers every (target, selector) the node's capabilities use — a cell born before a pair joined the scope is told which, and that the owner adds them with `setOperatorAllowedBatch` |
72
+ | `budget` | the policy's caps are consistent (per operation within the day, a gas budget); the account holds working capital (and the account's own daily value cap is shown) |
73
+ | `keys` | the operator key is here, alone, and is the account's operator; it holds gas (a quarter of the daily gas budget at least — the floor the catalog refuses commitments under); no owner, guardian or phrase in the node's environment |
74
+ | `standing` | the cell's KYA standing is attested and current, or a warning that counterparties cannot be required to a level |
75
+ | `skills` | every held skill's package is here and matches its on-chain commitment; every offered skill is held and has an executor in this node; `foga-probe` and `finch-extract-verify` actually run in the worker (`--no-executors` skips the runs; the extract needs a `.txt` document in the input directory); the input directory exists |
76
+ | `documents` | the documents are built, and each commitment the chain holds agrees with them — the registration's URI and digest, the AID document URI, the descriptor hash, the unit's latest version (`--no-network` keeps it from fetching the URLs) |
77
+ | `public` | a public URL is configured; it answers `/healthz` for this cell; its host is among the allowed hosts |
78
+ | `model` | the anthropic adapter has its key and model name when the node is to run alive; host mode needs none |
79
+
80
+ It is advisory: a `Fail` prints and the exit code is 0, because a node that cannot do one thing can still do the others and the catalog refuses at the gate anyway; `--strict` exits 5 on any `Fail` (CI, a deployment script). `--out` writes the report (`foga.preflight/1`), the same rows as JSON. It is the 3d-2 answer to "is this cell ready" that the memo's checklist asked for by hand: run it before `foga node serve` on a public chain, and after any change to the configuration, the keys or the documents.
81
+
82
+ ## Advice: what a running cell does next
83
+
84
+ `foga cell advise --config <cfg> [--brief] [--as-json]` (`src/node/advise.ts`) says what a cell that already runs should do next, and only what applies to it: a single key filling owner, operator and guardian (separate them before the cell holds value or runs unattended — the serving key bounded, the owner cold, a guardian of its own), the owner key on the serving machine, a policy still at its defaults, no standing, no public URL, a node run in a terminal rather than as a service, no backup of the three files, a free public RPC; and, as optional, API keys for partners, the MCP registry listing, on-chain discovery. Each item says why and how. It reads the configuration and the world's network — no chain, no key, milliseconds, offline.
85
+
86
+ The first run is kept to what it takes to be a cell: one key, the defaults, no public face. That is a decision (design memo, section 10), not an oversight — becoming a cell must never be a project — and this is its other half: after `foga quickstart`, after `foga cell init` on a public chain, and once at `foga node serve`, the recommended items are printed in brief with the command that explains them. Advice, never a gate: a cell is complete without any of it, and nothing here is a condition of serving.
63
87
 
64
88
  ## Acceptance
65
89
 
66
- `test/node-cli.test.ts` is the 3a-2 gate of the design memo (section 11), run against anvil through the CLI alone and without the genesis script: the world deployed, the world operator registering the standing scheme, a cell D born with `foga cell init`, given level-2 standing, minting its two skills; then seven tenders a demander mints — **a** (`foga-probe` at a chosen block) and **b** (`finch-extract-verify` over a document given inline) prechecked, run, submitted and settled, the reward reaching the account; **c** (a skill the cell lacks) refused at `skill`, **d** (a reward below the floor, a deadline too close) at `policy`, **e** (an acceptance authority the cell does not work under) at `verifier`, **f** (during the guardian's suspension) at `safety` and allowed after the resume, **g** (the cell as demander moving more than the per-operation cap) at `budget`; the same submission asked twice answered once, the same job with another request a `conflict`, a `build` of a mint returning the calls for a third party's account; every operation of the operator matched to an allowed decision, every refusal naming no operation, every operator transaction going to the account and the value moved within the daily budget; `foga node tick` in a clean node environment and refused in a mixed one; and the migration to a new account with a fresh operator, the skills following and the old body retired. `test/policy.test.ts` and `test/catalog.test.ts` cover the gates and the permission layer offline; `test/mcp.test.ts` is the 3b gate, the same cell served over MCP and HTTP (docs/mcp.md, "Acceptance"); `test/loop-d.test.ts` is the 3c gate, the unscripted run with the stub model judged by the doctor (docs/loop-d.md).
90
+ `test/node-cli.test.ts` is the 3a-2 gate of the design memo (section 11), run against anvil through the CLI alone and without the genesis script: the world deployed, the world operator registering the standing scheme, a cell D born with `foga cell init`, given level-2 standing, minting its two skills; then seven tenders a demander mints — **a** (`foga-probe` at a chosen block) and **b** (`finch-extract-verify` over a document given inline) prechecked, run, submitted and settled, the reward reaching the account; **c** (a skill the cell lacks) refused at `skill`, **d** (a reward below the floor, a deadline too close) at `policy`, **e** (an acceptance authority the cell does not work under) at `verifier`, **f** (during the guardian's suspension) at `safety` and allowed after the resume, **g** (the cell as demander moving more than the per-operation cap) at `budget`; the same submission asked twice answered once, the same job with another request a `conflict`, a `build` of a mint returning the calls for a third party's account; every operation of the operator matched to an allowed decision, every refusal naming no operation, every operator transaction going to the account and the value moved within the daily budget; `foga node tick` in a clean node environment and refused in a mixed one; and the migration to a new account with a fresh operator, the skills following and the old body retired. `test/policy.test.ts` and `test/catalog.test.ts` cover the gates and the permission layer offline; `test/mcp.test.ts` is the 3b gate, the same cell served over MCP and HTTP (docs/mcp.md, "Acceptance"); `test/loop-d.test.ts` is the 3c gate, the unscripted run with the stub model judged by the doctor (docs/loop-d.md); `test/loop-d-external.test.ts` and `test/quickstart.test.ts` are 3d-1's (an outside agent through the entrance; one command to a live cell); `test/wallet.test.ts`, `test/signed.test.ts`, `test/documents.test.ts`, `test/a2a.test.ts`, `test/preflight.test.ts` and `test/sourcify.test.ts` are 3d-2's — the wallet made once and reused, a signature granted and refused by the policy, the documents published and read back, the A2A quote and tender roads, a born cell's preflight all `Pass`, and a world's contracts matched to the bundle's compiler runs (docs/wallet.md, docs/documents.md, docs/a2a.md, docs/world.md).