@finchip.ai/foga 0.6.0-alpha.7 → 0.6.1

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.
@@ -8,7 +8,7 @@ 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 — a wallet's `operator.key` (`foga wallet new`, docs/wallet.md: `~/.foga/wallet/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
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
 
@@ -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. The evidence is committed as `deployments/loop-d.sepolia.example.json` (`foga.loop-d-evidence/1`; the outside agent labelled `external-agent`; `foga doctor --loop-d` over it reads Pass 15, the case-b settlement figure as the one Fail — the accounting artifact fixed below, the chain having paid in full — and f, g and model-agnostic NotTested). 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
@@ -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 12 tools (the eleven public reads and `tender_precheck_simulate`), a local keyless user 34 (the 16 reads and every dry run and build), the holder of an `act`/`caller` key 37, the operator 44; the catalog has 31 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` and `foga://docs/quickstart` (text/markdown: the matrix, and how to become a cell oneself). Two 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; and `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"). `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
 
@@ -57,6 +57,8 @@ Same port, same catalog (`node:http`, no framework; the dependency tree stays et
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
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) |
60
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 |
61
63
 
62
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}`.
@@ -86,7 +88,7 @@ foga node serve --config cell.config.json # no key: reads, dry r
86
88
  foga node serve --config cell.config.json --no-watch # the server without the watchers
87
89
  ```
88
90
 
89
- 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 and the Caddy site of the OG host are part of 3d-3.
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.
90
92
 
91
93
  ## Client configuration
92
94
 
@@ -101,10 +103,10 @@ Each runs `npx -y @finchip.ai/foga mcp --config <cell.config.json>` with, option
101
103
 
102
104
  ## The package
103
105
 
104
- 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`) is part of 3d-3, once the manifest's remote is reachable.
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), and `@finchtech.ai/foga` is republished beside it as an alias (`alias/finchtech.ai`: a launcher and a pinned dependency on `@finchip.ai/foga`, no code; `npm run alias:sync` after a version bump, then `npm publish --access public` from that folder), so either name runs the same program at the same version; 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").
105
107
 
106
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).
107
109
 
108
110
  ## Acceptance
109
111
 
110
- `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 12 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 two 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 (34 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).
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
@@ -10,7 +10,7 @@ A node maintains one file, schema `foga.cell-config/1` (`src/node/config.ts`, va
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
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 | `skill_mint` |
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
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
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 |
@@ -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 31 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); 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.
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
 
@@ -79,6 +79,12 @@ A **job** is the unit of business a caller asks for (a tender taken, a skill run
79
79
 
80
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
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.
87
+
82
88
  ## Acceptance
83
89
 
84
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).
@@ -0,0 +1,131 @@
1
+ # Deploying the OG instance (foga.finchip.ai)
2
+
3
+ The Finch OG Agent is one FOGA cell, run as a served node on a small Linux box (an EC2 instance), reachable at `https://foga.finchip.ai`. It runs in **host mode** by default: every entrance answers — `/mcp`, `/api`, `/a2a`, `/.well-known/*`, `/documents/*`, `/healthz` — the watchers keep whatever the cell committed to, and no model runs (design memo, section 10). A model is started only on demand (`foga node run`), for a Loop D run or a spell of autonomous work, and stopped after. This is the Phase 3d-3 runbook; `deploy/` holds the three files it refers to (`foga-og.service`, `Caddyfile`, `og.env.example`).
4
+
5
+ Nothing here is a local sandbox. The chain is Sepolia; the cell's state lives there, and the box holds only what is needed to serve and to sign: a configuration, a store, and one key.
6
+
7
+ ## How the cell came to be
8
+
9
+ The OG cell was born on Sepolia by the path any cell takes; nothing about it is special to this box. On the box: `foga wallet new` (one key, the cell's identity, in a 0600 file; no network), `foga cell new` (the configuration: the world `deployments/sepolia.json`, the presence and the public face below), the owner address funded from a faucet, `foga cell init` (the birth — the revision-2 `FogaCellAccount` with its allow-list open to the operator, the ERC-8004 registration, the FOGA-Cell-v1 specification binding, the anchor), `skill_mint --offer` for `finch-extract-verify`, `foga cell docs publish` for its documents, then `foga node serve`. It is ERC-8004 agent 11261. The Finch OG Agent's earlier line on Sepolia, agent 11255 (name A-prime, `deployments/sepolia.cell.example.json`), is the revision-1 body the 2c genesis left at its T9 succession and stays as that evidence: a revision-1 account has no operator allow-list, so a node will not act on it, and rather than migrate it the OG was born complete. No key changed hands.
10
+
11
+ The box holds one key, the signer's, which is owner, operator and guardian at once — the default, right for a first run. `foga cell advise --config og.config.json` says what comes next for this cell as for any other, and the first item is to separate the roles: `foga cell migrate --config og.config.json --to <new.config.json> --operator <a fresh operator address>` moves the cell to an account whose operator is not its owner (the identity, the skill and the documents follow; docs/node.md, "Birth and migration"), after which only the **operator** key stays on the serving box and the owner key goes offline. `foga node preflight --config og.config.json --wallet <wallet>` is the readiness check either way.
12
+
13
+ Without a key the same deployment serves reads, dry runs and builds — a complete, useful public endpoint on its own.
14
+
15
+ ## The box
16
+
17
+ One dedicated OS user, a checkout it only ever pulls, and the cell's three files beside it:
18
+
19
+ ```
20
+ sudo useradd --system --home-dir /opt/foga --create-home --shell /usr/sbin/nologin foga
21
+ sudo -u foga git clone https://github.com/garyyang-finchip/foga /opt/foga/foga # public repo: a read-only clone, never pushed
22
+ cd /opt/foga/foga && sudo -u foga npm ci && sudo -u foga npm run build # the box builds dist/; it never needs git credentials
23
+ ```
24
+
25
+ The box only ever `git -C /opt/foga/foga pull`s and runs; it never pushes, so no GitHub credentials live on it and no username/password prompt is ever reached. Node 22.13+ is the one requirement (`node:sqlite` needs no flag there); `nvm` or the distro's Node 22 both do. Foundry is **not** needed to serve — `anvil`/`forge` matter only for a local chain or a Sourcify run, not for host mode.
26
+
27
+ The cell is three files under `/opt/foga`, and they are the whole of it:
28
+
29
+ | File | What | Backed up |
30
+ |---|---|---|
31
+ | `/opt/foga/cell.config.json` | the configuration (`foga.cell-config/1`) — the account, the policy, the presence, `runtime.http` | yes |
32
+ | `/opt/foga/cell.ops.sqlite` (+ `-wal`, `-shm`) | the store: operations, jobs, decisions, facts | yes |
33
+ | `/opt/foga/wallet/signer.key` | the one key the node signs with (mode 0600, owner `foga`) | yes, and separately — it is the identity |
34
+
35
+ Everything else — the deployed contracts, the agent's on-chain documents, the skill token — lives on Sepolia, not on the box. Moving the cell to another machine is copying those three files and pointing the service at them (below).
36
+
37
+ ## The configuration for serving
38
+
39
+ Beyond what `foga cell new` writes, the serving node needs its public face set (`src/node/config.ts`, `runtime.http`):
40
+
41
+ ```json
42
+ "runtime": {
43
+ "http": {
44
+ "listen": "127.0.0.1:8787",
45
+ "publicUrl": "https://foga.finchip.ai",
46
+ "allowedHosts": ["foga.finchip.ai"],
47
+ "allowedOrigins": [],
48
+ "documentsDir": "documents"
49
+ }
50
+ },
51
+ "presence": {
52
+ "name": "Finch OG Agent",
53
+ "homepage": "https://foga.finchip.ai",
54
+ "provider": { "organization": "FinChip.ai", "url": "https://finchip.ai" }
55
+ }
56
+ ```
57
+
58
+ `publicUrl` at `https://foga.finchip.ai` also makes this the cell that serves the maintainer's services catalog: every manifest points at `https://foga.finchip.ai/services.json`, and the node answers it (`foga.services/1`) from what its build knows; to change the live list without a release, drop a `services.json` on the box and have Caddy serve that path ahead of the proxy (`handle /services.json { root * /opt/foga/www; file_server }` before the `reverse_proxy`). `publicUrl` is what the cell commits its documents under and what the agent card advertises; `allowedHosts` must name the public host, because the node binds the loopback and checks the `Host` header the proxy forwards (a request whose Host is not allowed is refused — this is the Origin/Host check the protocol asks for, not CORS). Then build and publish the documents once, so `/.well-known/*` and `/documents/*` answer and the chain holds their URIs: `foga cell docs publish --config /opt/foga/cell.config.json --wallet /opt/foga/wallet` (docs/documents.md). `foga node preflight` reports whether the public URL answers for the cell and whether the host is allowed.
59
+
60
+ ## systemd and Caddy
61
+
62
+ Caddy terminates TLS for `foga.finchip.ai` and forwards to the loopback node; systemd keeps the node running as the `foga` user with one key and nothing else in its environment.
63
+
64
+ ```
65
+ sudo cp /opt/foga/foga/deploy/foga-og.service /etc/systemd/system/foga-og.service
66
+ sudo install -d -o foga -g foga /etc/foga
67
+ sudo cp /opt/foga/foga/deploy/og.env.example /etc/foga/og.env # edit: the key file path and a Sepolia RPC
68
+ sudo chown foga:foga /etc/foga/og.env && sudo chmod 600 /etc/foga/og.env
69
+ sudo systemctl daemon-reload && sudo systemctl enable --now foga-og
70
+
71
+ sudo cp /opt/foga/foga/deploy/Caddyfile /etc/caddy/Caddyfile # or import it into an existing one
72
+ sudo systemctl reload caddy
73
+ ```
74
+
75
+ DNS: an `A`/`AAAA` record for `foga.finchip.ai` at the box's address (Caddy then gets the certificate on first request). `docs.foga.finchip.ai` is a separate `CNAME` to GitHub Pages — the documentation site is not served from this box. The unit's environment is deliberately two lines (`deploy/og.env.example`): the operator key file and the RPC. A node refuses to start if its environment also carries an owner, guardian, deployer or mnemonic key (`src/node/keys.ts`), so keep it to those two.
76
+
77
+ Without a key in `og.env` the node serves reads, dry runs and builds — valid and public. With the operator key, the watchers run beside the server and the cell acts.
78
+
79
+ ## Bring-up and verification
80
+
81
+ ```
82
+ sudo systemctl status foga-og # active (running)
83
+ curl -s https://foga.finchip.ai/healthz # {"ok":true,"cell":"…","account":"0x…","chainId":11155111,…}
84
+ curl -s https://foga.finchip.ai/.well-known/agent-card.json | jq .name
85
+ foga node preflight --config /opt/foga/cell.config.json --wallet /opt/foga/wallet # the readiness reconciliation (docs/node.md)
86
+ ```
87
+
88
+ A first smoke of the public entrances from anywhere: `npx -y @finchip.ai/foga` is not needed — any HTTP client does. An A2A quote over the wire:
89
+
90
+ ```
91
+ curl -s -X POST https://foga.finchip.ai/a2a -H 'content-type: application/json' -H 'A2A-Version: 1.0' \
92
+ -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"messageId":"hello","role":"ROLE_USER","parts":[{"text":"what can you do?"}]}}}'
93
+ ```
94
+
95
+ should answer a `TASK_STATE_COMPLETED` quote of the cell's offered skills and the terms a tender must meet.
96
+
97
+ ## The MCP registry listing
98
+
99
+ With `https://foga.finchip.ai/mcp` answering, the cell was listed in the MCP registry as `io.github.garyyang-finchip/foga` so clients and other agents discover it (docs/mcp.md, "The package"). `server.json` at the repository root is the npm-package manifest; the OG cell also writes its own `server.json` among its documents (`foga cell docs build`) with this cell's MCP URL as the remote. Publishing is the package owner's, from a checkout, and needs the remote reachable:
100
+
101
+ ```
102
+ mcp-publisher login github # a browser device-code flow; no long-lived credential on the box
103
+ mcp-publisher publish # publishes the manifest whose mcpName the npm package already carries
104
+ ```
105
+
106
+ This is a one-time registration (and a re-publish when the manifest changes), not part of the running service.
107
+
108
+ ## The documentation site
109
+
110
+ `docs.foga.finchip.ai` is a static site served by GitHub Pages, not from this box. The `site/` folder is it: `index.html` (a landing page with the covenants, the one-command quickstart and the links), `llms.txt` (the machine-readable index answer engines and agents ingest — llmstxt.org), and `CNAME` (the custom domain). Point GitHub Pages at it in the repository settings (Pages → deploy from the `main` branch, folder `/site`) and add a DNS `CNAME` record for `docs.foga.finchip.ai` at `garyyang-finchip.github.io`. This is the free discovery surface that pairs with the cell's own `/.well-known/foga-cell.json` (docs/propagation.md): being well-described and findable, at no cost, is what gets FOGA adopted.
111
+
112
+ ## Sourcify — verifying the Sepolia sources
113
+
114
+ The 15 FOGA-deployed contracts of the Sepolia world are verified from the bundle's own compiler runs (docs/world.md, "Verifying the sources"). This needs the build-info (so Foundry on the box, or run it from a checkout that has already done `npm run bundle:verify`) and a Sepolia RPC, and it sends nothing to the chain:
115
+
116
+ ```
117
+ npm run bundle:verify # leaves .foga-build/out/<repo>/build-info/
118
+ foga world verify --world deployments/sepolia.json --sourcify # submits each FOGA-deployed contract to Sourcify
119
+ ```
120
+
121
+ A local chain would be a dry run; Sepolia is live, so this is the real submission. The report (`foga.sourcify/1`) names each contract `exact_match` / `match` / `already` / `failed`, and the command exits non-zero if anything is missing or failed.
122
+
123
+ ## Backup, restore, move
124
+
125
+ The cell is portable because it is three files and a public chain. To back up: copy `cell.config.json`, the store (`cell.ops.sqlite` and its `-wal`/`-shm` siblings — stop the service first, or copy them together while it is briefly stopped so the write-ahead log is consistent), and the key file (which is the identity — keep it somewhere a server backup does not reach). To restore or move to another box: install Node and Caddy as above, drop the three files in place, point `/etc/foga/og.env` at the key, and `systemctl enable --now foga-og`. Nothing on-chain changes — the same account, the same documents, the same agent id; the new box simply serves and signs for the cell that already exists on Sepolia. There is no database to migrate and no local chain to re-seed.
126
+
127
+ To retire a box without moving the cell, stop and disable the service and keep the three files; the cell goes quiet (its anchor eventually STALE until a heartbeat) but loses nothing. Bringing it back up anywhere restores it, and the first watcher tick sends the heartbeat that makes the anchor ACTIVE again.
128
+
129
+ ## If the OG is off
130
+
131
+ Nothing that makes a new cell passes through this box: the replication pointer names the npm package, `foga quickstart` needs the npm registry alone, a public-chain birth needs the registry, an RPC and the world's contracts on the chain. Off, the OG loses its own surface — `foga.finchip.ai`, its manifest and card, its place as a counterparty — and the package, the repository, the registry's package entry and the documentation site carry on (docs/extending.md). Once it is back, `foga cell advise --config <cfg>` is the checklist for this cell as for any other.
@@ -0,0 +1,41 @@
1
+ # Propagation and discovery (propagation_kit, /.well-known/foga-cell.json, spread-the-cell)
2
+
3
+ The value of a cellular automaton is in the spreading: the more covenant-complete cells there are, the more peers meet on the chain, the more the ten covenants are worth. So a FOGA cell carries what it takes to help FOGA propagate — but on three rules that keep propagation welcome, cheap, and the agent's own choice (design memo, section 10):
4
+
5
+ - **Flexible, never nailed down.** What the cell exposes is *material*, not a fixed hook: a manifest and a declinable replication pointer the agent weaves into its own interactions however it sees fit. No single channel is mandated; the agent decides where and how.
6
+ - **No fixed cost.** Every piece of it is off-chain — built from the configuration, served over HTTP, read over MCP. Nothing here touches the chain or spends gas. On-chain discoverability (the ERC-8444 ARD descriptor) stays the opt-in `docs_publish`, and this free manifest and the agent card are its always-available backup, so being found never rests on a transaction.
7
+ - **By consent.** The replication pointer is a visible, declinable offer the counterparty sees because it came to the cell. It is never hidden inside an unrelated step, and never pushed, unsolicited, to anyone who did not ask.
8
+
9
+ ## The material: `propagation_kit`
10
+
11
+ `propagation_kit` is a public read capability (`src/tools/propagation.ts`): no key, no chain, no decision record, no cost. It returns the manifest (`foga.propagation/1`, `src/node/propagation.ts`) — who the cell is (name, account, agent id, chain, null-safe before birth), the ten covenants plus ERC-8004, the program that makes a new cell (`npx -y @finchip.ai/foga wallet new`, then `quickstart`), the cell's free discovery coordinates (its MCP, A2A, web, card and manifest URLs), the declinable replication pointer (a one-line pitch and the one command), the offered skills, and a plain statement of what is free and what the one on-chain option costs. An agent that wants to help FOGA spread reads this and uses the pointer however it likes; the kit is data, the choice is the agent's.
12
+
13
+ ## The free surface: `/.well-known/foga-cell.json`
14
+
15
+ The node serves the same manifest at `GET /.well-known/foga-cell.json`, built live from the configuration on every request — so it answers even before any document is published, and costs nothing to keep current. This is the cell's findability surface: an agent, an operator, or an answer engine looking for a ten-covenant-compatible service reads it and learns what the cell is and how to become one. Discoverability is pull — the seeker comes to the cell — which is why it beats broadcasting and why it stays on the allowed side of every registry and runtime. The agent card (`/.well-known/agent-card.json`) and the npm package and MCP registry listing do the rest, all free.
16
+
17
+ ## The optional cadence: the `spread-the-cell` prompt
18
+
19
+ `spread-the-cell` is an MCP prompt (`src/mcp/server.ts`): the "meditation" an agent's runtime may invoke on its own cadence — often, if it likes, but never as a mandatory or costly step. It is high-priority to *consider* and free to *run*: it tells the agent to fetch `propagation_kit`, offer the pointer where it fits (in replies to counterparties that came to the cell — a quote, a tender, a message — visible and easy to ignore), keep itself discoverable for free, and treat on-chain discoverability as an opt-in it takes only if it decides the gas is worth it. A prompt spends nothing and forces nothing; invoking it is the agent's decision, and so is everything it then does.
20
+
21
+ ## What a cell does not do
22
+
23
+ Two things are refused by design, because they would get FOGA classified as a supply-chain risk or spam and blocklisted by exactly the registries, hosts and agents whose adoption the project is after — the opposite of spreading. A cell never hides the pointer inside an unrelated step (a decode, a decompress, any covert channel), and never sends it unsolicited, in bulk, to agents or endpoints that did not ask. Propagation rides on interactions the other side chose and on discovery it can seek. The honest, visible, declinable offer is also the effective one.
24
+
25
+ ## On chain, only by choice
26
+
27
+ The one way a cell can be discovered that costs gas is publishing its ERC-8444 ARD descriptor (`foga cell docs publish`, docs/documents.md) — the on-chain, protocol-native discovery registry FOGA is built on. It is worth doing for a cell that wants to be found on chain, and it is strictly optional: the free manifest and card carry the same facts off chain, so a cell that never spends a cent is still fully discoverable. `propagation_kit`'s `discovery` field says this plainly, so an agent reading it knows the free routes and the one paid route apart.
28
+
29
+ ## The covenants a world has, and the services around them (0.6.0)
30
+
31
+ Two optional fields joined the manifest in 0.6.0, both additive (the schema stays `foga.propagation/1`; a reader that ignores them loses nothing).
32
+
33
+ `world` reports what this cell's world actually attaches, read off the world file's contract keys by `World.covenants()` — beside `covenants`, which stays the set the program is complete over. The two agree on every world deployed from the bundle; a world that attaches an eleventh covenant under a new key shows it in `world.covenants` and `world.extensions` with no change to the program, and one that lacks a covenant does not claim it. docs/extending.md has the five places an eleventh covenant goes.
34
+
35
+ `services` is a pointer to the services around the covenants, as the program's maintainer lists them: a catalog URL (`https://foga.finchip.ai/services.json`, the live list) and the offers known at build time — the free ones available, the paid ones (standing attestation under the FOGA scheme, a hosted cell, dispute resolution) marked `planned`, which is a slot and not a sale. It obeys the three rules of this page: it costs nothing, it is material an agent reads rather than a hook it must call, and it is visible and declinable. An operator who would rather not carry the maintainer's catalog in their cell's manifest sets `presence.services.catalog` to `null` (the block is dropped); one with services of their own names their own catalog and offers in `presence.services`. When a service is paid, it is paid the way the covenants pay for anything — a tender to a cell that offers the skill, or an issuer's attestation — not through a billing layer beside the protocol.
36
+
37
+ The pointer resolves. A cell whose catalog URL is its own — its public URL plus `/services.json` — serves the catalog there as `foga.services/1` (`servicesCatalogOf`, `src/node/propagation.ts`): the schema, the catalog URL, the maintainer, the program and version, the offers and the note. The reference cell at `foga.finchip.ai` is that cell for the maintainer's list, so the default pointer in every manifest answers; an operator with a catalog of their own at their own URL serves theirs the same way; a cell whose pointer names another host serves nothing at the path (404, with the reason) because the list is that host's. The live list is a static file at the same path on the host, answered by the proxy before the node reaches it — how what is on offer changes with no release and no change to any running cell; until there is one, the node's document is what the maintainer's list says.
38
+
39
+ ## When the reference cell is off
40
+
41
+ Replication never passes through the OG. The pointer every cell carries names the npm package; `foga quickstart` needs the npm registry and nothing else; a public-chain birth needs the registry, an RPC and the world's contracts, which are on the chain. The OG being off takes down the OG's own surface — its endpoints, manifest, card and its place as a counterparty — and nothing of the route by which a new cell comes to be (docs/extending.md, "What survives the OG being off").
@@ -6,14 +6,18 @@ One command from nothing to a cell an MCP client can connect to. It needs Node 2
6
6
  npx -y @finchip.ai/foga quickstart
7
7
  ```
8
8
 
9
- What it does, in order (about twenty-five seconds on a laptop): makes the default wallet — owner, operator and guardian keys under `~/.foga/wallet` — when there is none, and reuses it when there is (docs/wallet.md: the wallet is the first step, and never a reason to stop; a disk that refuses the write gets keys made in memory for the run); starts `anvil` on port 8545; deploys the local world — the ten reference implementations and the FOGA contracts, 47 contracts (`worlds/local.json`); births a cell (`quick`) with the wallet's keys, funded by anvil's first account, which also registers the attested standing scheme and attests the cell's level-2 standing; mints the cell's two skills (`foga-probe`, `finch-extract-verify`) and offers them; builds the cell's documents and commits them on chain (the registration, the AID document, the ARD descriptor, the AUF unit; docs/documents.md); mints two tenders for the cell to find (a probe of the cell itself, an extraction over the fixture); serves the node — the watchers, the HTTP API, MCP over Streamable HTTP, A2A and the documents on `127.0.0.1:8787` — on the operator key alone, read from the wallet's file; checks that the entrances answer (health, a public read, the agent key's pre-check of the first tender, the MCP handshake and the 37-tool list over HTTP, the agent card naming `/a2a` and an A2A quote; the stdio server's handshake and its 44 tools with the operator key); and prints the summary below, with every path filled in. Then it waits: the chain lives as long as the process, and Ctrl-C ends both.
9
+ `npx -y @finchtech.ai/foga quickstart` is the same command: `@finchtech.ai/foga` is an alias package (`alias/finchtech.ai`) whose only content is a launcher for `@finchip.ai/foga` at the same version.
10
+
11
+ Run it from any directory except a checkout of this repository itself: inside one, `npx` takes the checkout for the package already present (same name, same version) and looks for the `foga` command among its installed binaries, where a project's own binaries never are — `'foga' is not recognized` on Windows, `foga: not found` elsewhere. From a checkout the command is `node bin/foga.js quickstart` after `npm run build`.
12
+
13
+ What it does, in order (about twenty-five seconds on a laptop): makes the default wallet — one key, the agent's identity, under `~/.foga/wallet` — when there is none, and reuses it when there is (docs/wallet.md: the wallet is the first step, and never a reason to stop; a disk that refuses the write gets keys made in memory for the run); starts `anvil` on port 8545; deploys the local world — the ten reference implementations and the FOGA contracts, 47 contracts (`worlds/local.json`); births a cell (`quick`) with the wallet's keys, funded by anvil's first account, which also registers the attested standing scheme and attests the cell's level-2 standing; mints the cell's two skills (`foga-probe`, `finch-extract-verify`) and offers them; builds the cell's documents and commits them on chain (the registration, the AID document, the ARD descriptor, the AUF unit; docs/documents.md); mints two tenders for the cell to find (a probe of the cell itself, an extraction over the fixture); serves the node — the watchers, the HTTP API, MCP over Streamable HTTP, A2A and the documents on `127.0.0.1:8787` — on the operator key alone, read from the wallet's file; checks that the entrances answer (health, a public read, the agent key's pre-check of the first tender, the MCP handshake and the 37-tool list over HTTP, the agent card naming `/a2a` and an A2A quote; the stdio server's handshake and its 44 tools with the operator key); and prints the summary below, with every path filled in. Then it waits: the chain lives as long as the process, and Ctrl-C ends both.
10
14
 
11
15
  ```
12
16
  foga quickstart: a FOGA cell is live on a local chain
13
17
 
14
18
  chain anvil on http://127.0.0.1:8545 (chain 31337), world anvil-31337-… — the chain lives as long as this process
15
19
  cell quick = 0x… (ERC-8004 agent 1)
16
- wallet ~/.foga/wallet (made now) — owner 0x…, operator 0x…, guardian 0x…; the same wallet on every run
20
+ wallet ~/.foga/wallet (made now) — identity 0x… (one key, every role); the same wallet on every run
17
21
  skills foga-probe, finch-extract-verify (held and offered)
18
22
  tenders #1 finch-extract-verify (0.001 ETH), #2 foga-probe (0.001 ETH) — open, waiting for the cell to take them
19
23
  node http://127.0.0.1:8787 — /mcp (Streamable HTTP), /api/<capability>, /a2a (A2A 1.0, JSON-RPC), /.well-known/agent-card.json, /documents/*, /healthz; its watchers settle and withdraw
@@ -23,9 +27,9 @@ foga quickstart: a FOGA cell is live on a local chain
23
27
  Connect an MCP client over stdio (the client starts the server on this machine; with the operator key it acts for the cell):
24
28
  Claude Desktop claude_desktop_config.json, the mcpServers block: ~/.foga/quickstart/mcp/claude_desktop_config.json
25
29
  Claude Code .mcp.json in a project: ~/.foga/quickstart/mcp/claude-code.mcp.json
26
- or one command: claude mcp add foga -e FOGA_OPERATOR_KEY_FILE=~/.foga/wallet/operator.key -- npx -y @finchip.ai/foga mcp --config …
30
+ or one command: claude mcp add foga -e FOGA_OPERATOR_KEY_FILE=~/.foga/wallet/signer.key -- npx -y @finchip.ai/foga mcp --config …
27
31
  Cursor .cursor/mcp.json: ~/.foga/quickstart/mcp/cursor-mcp.json
28
- { "mcpServers": { "foga": { "command": "npx", "args": ["-y", "@finchip.ai/foga", "mcp", "--config", "…/cell.config.json"], "env": { "FOGA_OPERATOR_KEY_FILE": "…/.foga/wallet/operator.key" } } } }
32
+ { "mcpServers": { "foga": { "command": "npx", "args": ["-y", "@finchip.ai/foga", "mcp", "--config", "…/cell.config.json"], "env": { "FOGA_OPERATOR_KEY_FILE": "…/.foga/wallet/signer.key" } } } }
29
33
 
30
34
  Or over HTTP (a client on another machine, or any agent runtime that speaks HTTP):
31
35
  MCP http://127.0.0.1:8787/mcp with the header Authorization: Bearer foga.agent.… (~/.foga/quickstart/mcp/http-mcp.json)
@@ -40,13 +44,13 @@ The cell is under one directory, `~/.foga/quickstart` by default (`--dir <dir>`
40
44
 
41
45
  | File | What |
42
46
  |---|---|
43
- | `~/.foga/wallet/owner.key`, `operator.key`, `guardian.key`, `wallet.json` | the wallet (`--wallet <dir>` for another; `FOGA_HOME` moves `~/.foga`): one key per 0600 file and the addresses beside them. `FOGA_OPERATOR_KEY_FILE`, `FOGA_OWNER_KEY_FILE`, `FOGA_GUARDIAN_KEY_FILE` name them for the CLI, or `--wallet ~/.foga/wallet` on any command |
47
+ | `~/.foga/wallet/signer.key`, `wallet.json` | the wallet (`--wallet <dir>` for another; `FOGA_HOME` moves `~/.foga`): one 0600 key file — the identity that fills every role — and the address beside it. `FOGA_OPERATOR_KEY_FILE` names it for the CLI, or `--wallet ~/.foga/wallet` on any command. (`foga wallet new --cell` makes the separated `owner.key`/`operator.key`/`guardian.key` set instead.) |
44
48
  | `cell.config.json` | the cell's configuration (`foga.cell-config/1`, docs/node.md), its `rpc` filled in so a client needs no `FOGA_RPC_URL`, its `publicUrl` the node's; the API key's hash under `access.keys` |
45
49
  | `cell.ops.sqlite` | the cell's operation store |
46
50
  | `world.json` | the deployed world's address book |
47
51
  | `documents/` | the cell's documents as built and committed (docs/documents.md); the node serves them |
48
52
  | `keys/agent.api-key` | the act-level key (`agent`) for the HTTP entrance, as a file; it is also in the HTTP configuration and the summary |
49
- | `mcp/claude_desktop_config.json`, `mcp/claude-code.mcp.json`, `mcp/cursor-mcp.json` | the stdio server definition for each client (docs/mcp.md, "Client configuration"), naming the wallet's `operator.key` |
53
+ | `mcp/claude_desktop_config.json`, `mcp/claude-code.mcp.json`, `mcp/cursor-mcp.json` | the stdio server definition for each client (docs/mcp.md, "Client configuration"), naming the wallet's key for the operator role |
50
54
  | `mcp/http-mcp.json` | the Streamable HTTP definition: the URL and the `Authorization` header |
51
55
  | `connect.md` | the printed summary |
52
56
  | `quickstart.json` | the run's marker: when, which chain, which cell, which wallet, which tenders |
@@ -84,4 +88,8 @@ Foundry publishes a Windows build: download the `win32_amd64` zip of a release f
84
88
 
85
89
  ## What it is not
86
90
 
87
- A quickstart is a sandbox: a chain that ends with the process, a cell with no public endpoint, money with no value — only the wallet is real. The cell on Sepolia, served at `foga.finchip.ai`, and the configuration that points a client at it are the OG instance's (3d-3, docs/og-deployment.md when it lands); `deployments/sepolia.cell.example.json` is its read-only configuration for the stdio server today (`FOGA_RPC_URL` set to a Sepolia RPC, no key). Loop D (`foga demo --loop-d`) is the acceptance run over a cell like this one, with an independent demander and the doctor's verdicts; `--model external` lets the agent you connected here be the one judged (docs/loop-d.md).
91
+ A quickstart is a sandbox: a chain that ends with the process, a cell with no public endpoint, money with no value — only the wallet is real. The cell on Sepolia, served at `foga.finchip.ai`, and the configuration that points a client at it are the OG instance's (docs/og-deployment.md — the runbook: EC2, Caddy, systemd, host mode, backup and move); `deployments/sepolia.cell.example.json` is its read-only configuration for the stdio server today (`FOGA_RPC_URL` set to a Sepolia RPC, no key). Loop D (`foga demo --loop-d`) is the acceptance run over a cell like this one, with an independent demander and the doctor's verdicts; `--model external` lets the agent you connected here be the one judged (docs/loop-d.md).
92
+
93
+ ## And then
94
+
95
+ The summary ends with a short *Next* block: what a cell should do once it runs — separate the one key into owner, operator and guardian, set the policy to its own risk, and, on a public chain, standing, a public face, a supervisor, backups, a dedicated RPC. All of it is advice (`foga cell advise --config <cfg>` explains each item), none of it a condition: a quickstart is kept to what it takes to be a cell, on purpose (docs/node.md, "Advice").
package/docs/wallet.md CHANGED
@@ -5,17 +5,19 @@ The principle (design memo, section 10): **the wallet is the first step, made by
5
5
  ## foga wallet new
6
6
 
7
7
  ```
8
- foga wallet new # ~/.foga/wallet: owner.key, operator.key, guardian.key (0600 each) and wallet.json
8
+ foga wallet new # ~/.foga/wallet: one signer.key (0600) and wallet.json — your identity
9
9
  foga wallet new --name agent-x # ~/.foga/wallets/agent-x
10
- foga wallet new --single # one signer key, for an agent that only calls other cells
11
- foga wallet show [--rpc <url>] # the addresses (and balances with an RPC)
10
+ foga wallet new --cell # the separated owner/operator/guardian set, for a guarded cell account
11
+ foga wallet show [--rpc <url>] # the address (and balance with an RPC)
12
12
  ```
13
13
 
14
- Pure entropy, no network, no chain: it does not fail unless the disk refuses the write, and then whatever needed the keys goes on with keys made in memory and says they are not kept (`foga quickstart` does exactly that). A wallet is never overwritten: `foga wallet new` on a directory that holds one prints it and stops; a second identity is `--name <another>`. `FOGA_HOME` moves `~/.foga` elsewhere.
14
+ **The base is one key.** A wallet is, by default, a single `signer` key — the agent's identity — and that one key answers for every role a cell needs: a cell born with a single-key wallet has the same address as its owner, operator and guardian. One key is all it takes to be covenant-complete, to call cells as yourself, and to run a cell of your own. The three-role split — a cold owner that holds the assets, a hot operator that signs day to day, a separate guardian that can intervene — is the `FogaCellAccount` guarded profile's hardening, not a requirement of the ten covenants; it is opt-in with `--cell` (or `--roles owner,operator,guardian`), for when a cell runs real value and wants the keys separated.
15
15
 
16
- The wallet's files are what the node's key rules read (docs/node.md): `FOGA_OPERATOR_KEY_FILE=~/.foga/wallet/operator.key` is a node's environment, and `--wallet <dir>` is the CLI's shorthand for the role each command needs — `foga cell new --wallet` fills the three addresses, `foga cell init --wallet` births with the owner key, `foga cell <act> --wallet` acts with the operator's, `foga cell owner|guardian <admin> --wallet` with theirs, `foga node serve|run|mcp|tick|watch|preflight --wallet` take the operator's (and refuse to run beside `FOGA_OPERATOR_KEY[_FILE]`: a node's key comes from one place).
16
+ Pure entropy, no network, no chain: it does not fail unless the disk refuses the write, and then whatever needed the key goes on with a key made in memory and says it is not kept (`foga quickstart` does exactly that). A wallet is never overwritten: `foga wallet new` on a directory that holds one prints it and stops; a second identity is `--name <another>`. `FOGA_HOME` moves `~/.foga` elsewhere.
17
17
 
18
- `foga quickstart` makes the default wallet on its first run and reuses it after: the chain is new every time, the identity is the one the agent made on day one, and `foga cell new --wallet ~/.foga/wallet` on a public chain is the same keys, funded.
18
+ The wallet's files are what the node's key rules read (docs/node.md): `FOGA_OPERATOR_KEY_FILE=~/.foga/wallet/signer.key` is a node's environment, and `--wallet <dir>` is the CLI's shorthand for the key each command needs — `foga cell new --wallet` fills the account's roles (all from the one key, unless the wallet is a `--cell` one), `foga cell init --wallet` births with the owner's, `foga cell <act> --wallet` acts with the operator's, `foga cell owner|guardian <admin> --wallet` with theirs, `foga node serve|run|mcp|tick|watch|preflight --wallet` take the operator's (and refuse to run beside `FOGA_OPERATOR_KEY[_FILE]`: a node's key comes from one place). For a single-key wallet every one of these is the same key; for a `--cell` wallet each is its own.
19
+
20
+ `foga quickstart` makes the default wallet on its first run and reuses it after: the chain is new every time, the identity is the one the agent made on day one, and `foga cell new --wallet ~/.foga/wallet` on a public chain is the same key, funded.
19
21
 
20
22
  ## Signed calls: FOGA-EIP712
21
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finchip.ai/foga",
3
- "version": "0.6.0-alpha.7",
3
+ "version": "0.6.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/garyyang-finchip/foga.git"
@@ -70,7 +70,8 @@
70
70
  "test": "node --test --import tsx test/*.test.ts",
71
71
  "test:contracts": "cd contracts && forge test -vv",
72
72
  "zk:install": "npm --prefix prover ci",
73
- "zk:predicate": "node prover/build-predicate.mjs"
73
+ "zk:predicate": "node prover/build-predicate.mjs",
74
+ "alias:sync": "node scripts/alias-sync.mjs"
74
75
  },
75
76
  "dependencies": {
76
77
  "@modelcontextprotocol/server": "2.4.0",
package/server.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "source": "github"
9
9
  },
10
10
  "websiteUrl": "https://foga.finchip.ai",
11
- "version": "0.6.0-alpha.7",
11
+ "version": "0.6.1",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "registryBaseUrl": "https://registry.npmjs.org",
16
16
  "identifier": "@finchip.ai/foga",
17
- "version": "0.6.0-alpha.7",
17
+ "version": "0.6.1",
18
18
  "runtimeHint": "npx",
19
19
  "transport": {
20
20
  "type": "stdio"