@finchip.ai/foga 0.6.0-alpha.6 → 0.6.0-alpha.7
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.
- package/README.md +31 -14
- package/dist/a2a/card.js +54 -0
- package/dist/a2a/server.js +276 -0
- package/dist/a2a/types.js +20 -0
- package/dist/build/bundle.js +2 -1
- package/dist/cli-cell.js +147 -16
- package/dist/cli.js +138 -5
- package/dist/demo/birth.js +4 -4
- package/dist/demo/quickstart.js +60 -7
- package/dist/genesis/operator-scope.js +1 -0
- package/dist/http/access.js +105 -0
- package/dist/http/server.js +65 -43
- package/dist/http/signed.js +136 -0
- package/dist/mcp/server.js +27 -1
- package/dist/node/config.js +34 -1
- package/dist/node/documents.js +298 -0
- package/dist/node/preflight.js +208 -0
- package/dist/node/wallet.js +118 -0
- package/dist/tools/documents.js +74 -0
- package/dist/tools/index.js +2 -1
- package/dist/world/sourcify.js +165 -0
- package/docs/a2a.md +31 -0
- package/docs/design-memo.md +17 -6
- package/docs/documents.md +25 -0
- package/docs/examples/README.md +2 -2
- package/docs/mcp.md +16 -11
- package/docs/node.md +28 -10
- package/docs/quickstart.md +30 -21
- package/docs/wallet.md +60 -0
- package/docs/world.md +12 -0
- package/package.json +1 -1
- package/server.json +2 -2
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 |
|
|
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
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` |
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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,28 @@ 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
|
|
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.
|
|
63
81
|
|
|
64
82
|
## Acceptance
|
|
65
83
|
|
|
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).
|
|
84
|
+
`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).
|
package/docs/quickstart.md
CHANGED
|
@@ -1,73 +1,82 @@
|
|
|
1
1
|
# Quickstart (foga quickstart)
|
|
2
2
|
|
|
3
|
-
One command from nothing to a cell an MCP client can connect to. It needs Node 22.13 or newer and Foundry's `anvil`; nothing else
|
|
3
|
+
One command from nothing to a cell an MCP client can connect to. It needs Node 22.13 or newer and Foundry's `anvil`; nothing else. The keys it uses are a wallet it makes for you on the first run and keeps.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
npx -y @finchip.ai/foga quickstart
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
What it does, in order (about twenty seconds on a laptop): 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
|
|
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.
|
|
10
10
|
|
|
11
11
|
```
|
|
12
12
|
foga quickstart: a FOGA cell is live on a local chain
|
|
13
13
|
|
|
14
14
|
chain anvil on http://127.0.0.1:8545 (chain 31337), world anvil-31337-… — the chain lives as long as this process
|
|
15
|
-
cell quick = 0x… (ERC-8004 agent 1)
|
|
15
|
+
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
|
|
16
17
|
skills foga-probe, finch-extract-verify (held and offered)
|
|
17
18
|
tenders #1 finch-extract-verify (0.001 ETH), #2 foga-probe (0.001 ETH) — open, waiting for the cell to take them
|
|
18
|
-
node http://127.0.0.1:8787 — /mcp (Streamable HTTP), /api/<capability>, /healthz; its watchers settle and withdraw
|
|
19
|
+
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
|
|
20
|
+
documents ~/.foga/quickstart/documents — registration, AID document, ARD descriptor, AUF unit, agent card, server.json; committed on chain as URLs under http://127.0.0.1:8787
|
|
19
21
|
files ~/.foga/quickstart/cell.config.json (the cell), ~/.foga/quickstart/connect.md (this text), ~/.foga/quickstart/mcp/*.json (the client configurations)
|
|
20
22
|
|
|
21
23
|
Connect an MCP client over stdio (the client starts the server on this machine; with the operator key it acts for the cell):
|
|
22
24
|
Claude Desktop claude_desktop_config.json, the mcpServers block: ~/.foga/quickstart/mcp/claude_desktop_config.json
|
|
23
25
|
Claude Code .mcp.json in a project: ~/.foga/quickstart/mcp/claude-code.mcp.json
|
|
24
|
-
or one command: claude mcp add foga -e FOGA_OPERATOR_KEY_FILE
|
|
26
|
+
or one command: claude mcp add foga -e FOGA_OPERATOR_KEY_FILE=~/.foga/wallet/operator.key -- npx -y @finchip.ai/foga mcp --config …
|
|
25
27
|
Cursor .cursor/mcp.json: ~/.foga/quickstart/mcp/cursor-mcp.json
|
|
26
|
-
{ "mcpServers": { "foga": { "command": "npx", "args": ["-y", "@finchip.ai/foga", "mcp", "--config", "…/cell.config.json"], "env": { "FOGA_OPERATOR_KEY_FILE": "
|
|
28
|
+
{ "mcpServers": { "foga": { "command": "npx", "args": ["-y", "@finchip.ai/foga", "mcp", "--config", "…/cell.config.json"], "env": { "FOGA_OPERATOR_KEY_FILE": "…/.foga/wallet/operator.key" } } } }
|
|
27
29
|
|
|
28
30
|
Or over HTTP (a client on another machine, or any agent runtime that speaks HTTP):
|
|
29
31
|
MCP http://127.0.0.1:8787/mcp with the header Authorization: Bearer foga.agent.… (~/.foga/quickstart/mcp/http-mcp.json)
|
|
30
|
-
API http://127.0.0.1:8787/api/<capability>, POST a JSON body (reads also GET); the same header for acts.
|
|
32
|
+
API http://127.0.0.1:8787/api/<capability>, POST a JSON body (reads also GET); the same header for acts. GET http://127.0.0.1:8787/api/capabilities lists them.
|
|
33
|
+
A2A POST http://127.0.0.1:8787/a2a with A2A-Version: 1.0 — SendMessage for a quote (anyone) or a tender ({"foga":{"mode":"tender","tokenId":"…"}}, the header above); the card is at http://127.0.0.1:8787/.well-known/agent-card.json
|
|
34
|
+
wallet a credential of your own instead of the key: Authorization: FOGA-EIP712 <envelope> signed by any wallet (foga wallet sign --url http://127.0.0.1:8787 prints one; docs/wallet.md)
|
|
31
35
|
```
|
|
32
36
|
|
|
33
37
|
## The files
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
The cell is under one directory, `~/.foga/quickstart` by default (`--dir <dir>` for another; a directory that is not a quickstart's — no `quickstart.json` in it — is refused rather than overwritten), and the wallet outside it, where it survives every rerun:
|
|
36
40
|
|
|
37
41
|
| File | What |
|
|
38
42
|
|---|---|
|
|
39
|
-
| `
|
|
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 |
|
|
44
|
+
| `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` |
|
|
40
45
|
| `cell.ops.sqlite` | the cell's operation store |
|
|
41
46
|
| `world.json` | the deployed world's address book |
|
|
42
|
-
| `
|
|
47
|
+
| `documents/` | the cell's documents as built and committed (docs/documents.md); the node serves them |
|
|
43
48
|
| `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 |
|
|
44
|
-
| `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") |
|
|
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` |
|
|
45
50
|
| `mcp/http-mcp.json` | the Streamable HTTP definition: the URL and the `Authorization` header |
|
|
46
51
|
| `connect.md` | the printed summary |
|
|
47
|
-
| `quickstart.json` | the run's marker: when, which chain, which cell, which tenders |
|
|
48
|
-
| `anvil.log` | anvil's output |
|
|
52
|
+
| `quickstart.json` | the run's marker: when, which chain, which cell, which wallet, which tenders |
|
|
53
|
+
| `demander.sqlite`, `quickstart.sqlite`, `anvil.log` | the demander's store, the funder's, anvil's output |
|
|
49
54
|
|
|
50
|
-
Anvil's money has no value
|
|
55
|
+
Anvil's money has no value; the wallet is the one thing here that is not a sandbox. It is the identity the cell was born with and the same keys a cell on a public chain can be born with (`foga cell new --wallet ~/.foga/wallet`, then `foga cell init --wallet …`, once the owner and the operator are funded), so it is kept, never overwritten, and worth backing up like any key.
|
|
51
56
|
|
|
52
|
-
##
|
|
57
|
+
## Three ways in
|
|
53
58
|
|
|
54
|
-
**stdio.** The client starts `npx -y @finchip.ai/foga mcp --config ~/.foga/quickstart/cell.config.json` itself, with `FOGA_OPERATOR_KEY_FILE` in its environment: the server holds the operator key and acts for the cell (
|
|
59
|
+
**stdio.** The client starts `npx -y @finchip.ai/foga mcp --config ~/.foga/quickstart/cell.config.json` itself, with `FOGA_OPERATOR_KEY_FILE` in its environment: the server holds the operator key and acts for the cell (44 tools). Without the key the client gets reads, dry runs and builds (the `_simulate` and `_build` tools) and nothing it says can move the cell. The stdio server is a process of its own beside the served node; both write the same store (SQLite in WAL mode with a busy timeout), and the node's watchers settle and withdraw whatever either of them commits to.
|
|
55
60
|
|
|
56
|
-
**HTTP.** `http://127.0.0.1:8787/mcp` with `Authorization: Bearer <the agent key>` is the same server over Streamable HTTP, for a client that speaks it (Claude Code: `claude mcp add --transport http foga http://127.0.0.1:8787/mcp --header "Authorization: Bearer <key>"`; Cursor and others: `mcp/http-mcp.json`), and `POST http://127.0.0.1:8787/api/<capability>` with the same header is the plain JSON API for any agent runtime — `GET /api/capabilities` lists them, `foga cell schema <capability>` prints one's input and output schemas. Without the header the public reads and builds. `--listen 0.0.0.0:8787` opens the node to other machines (the key is then the only thing between them and the cell's acts; the chain is anvil's, so the stake is a demonstration); `--no-serve` runs no node at all, leaving the stdio server alone to the clients.
|
|
61
|
+
**HTTP.** `http://127.0.0.1:8787/mcp` with `Authorization: Bearer <the agent key>` is the same server over Streamable HTTP, for a client that speaks it (Claude Code: `claude mcp add --transport http foga http://127.0.0.1:8787/mcp --header "Authorization: Bearer <key>"`; Cursor and others: `mcp/http-mcp.json`), and `POST http://127.0.0.1:8787/api/<capability>` with the same header is the plain JSON API for any agent runtime — `GET /api/capabilities` lists them, `foga cell schema <capability>` prints one's input and output schemas. Without the header the public reads and builds. An agent with a wallet of its own needs no key from this cell: `Authorization: FOGA-EIP712 <envelope>` — `foga wallet sign --url http://127.0.0.1:8787` prints one — makes it a caller under its own address, at the level the cell's `access.signers` grants (docs/wallet.md). `--listen 0.0.0.0:8787` opens the node to other machines (the key is then the only thing between them and the cell's acts; the chain is anvil's, so the stake is a demonstration); `--no-serve` runs no node at all, leaving the stdio server alone to the clients.
|
|
62
|
+
|
|
63
|
+
**A2A.** `POST http://127.0.0.1:8787/a2a` with `A2A-Version: 1.0` is the Agent2Agent binding: `SendMessage` with words is a quote of what the cell offers and on what terms, open to anyone; a message carrying `{"foga": {"mode": "tender", "tokenId": "…"}}` from an act-level caller (the agent key, or a request-bound signature) takes an existing funded tender — pre-check, run, submit — as one task (docs/a2a.md). The card at `/.well-known/agent-card.json` says all this to an agent that has never seen a FOGA cell.
|
|
57
64
|
|
|
58
65
|
## What a connected agent does
|
|
59
66
|
|
|
60
67
|
A tender is worked in five steps, the same for every caller: `tender_list` (the open tenders), `tender_inspect {tokenId}` (the terms and the task document — `task.skill`, `task.input`), `tender_precheck {tokenId}` (every gate; `eligible: false` names the gate and is the cell's verdict), `skill_run {skill, input, tokenId}` (the run; `resultHash`, `runReceipt`), `tender_submit {tokenId, resultHash, runReceipt}`. The node's watchers settle the submission once the chain lets them and withdraw credited reward; `tender_settle` and `credit_withdraw` are offered too. An agent that goes through the two tenders sees 0.002 ETH of anvil's money arrive at the cell's account.
|
|
61
68
|
|
|
62
|
-
More tenders: the summary prints the demander's command line with every value filled in (`foga demo demander … --cases a,b --seed <n>`; `--cases c,d,d2,e` mints tenders the cell must refuse, docs/loop-d.md). The owner's and guardian's commands work with the
|
|
69
|
+
More tenders: the summary prints the demander's command line with every value filled in (`foga demo demander … --cases a,b --seed <n>`; `--cases c,d,d2,e` mints tenders the cell must refuse, docs/loop-d.md). The owner's and guardian's commands work with the wallet: `foga cell guardian guardian_suspend --config … --wallet ~/.foga/wallet --reason review --restrictions '["noNewRisk","deriskOnly"]'` suspends the binding; a pre-check then answers `safety`. `foga node preflight --config ~/.foga/quickstart/cell.config.json --wallet ~/.foga/wallet` reads the cell back the way a deployment would (docs/node.md, "Preflight").
|
|
70
|
+
|
|
71
|
+
An agent that met this cell through its MCP entrance can read how to make itself one: the prompt `become-a-cell` and the resource `foga://docs/quickstart` are this page.
|
|
63
72
|
|
|
64
73
|
## Options
|
|
65
74
|
|
|
66
75
|
```
|
|
67
|
-
foga quickstart [--dir <dir>] [--port <n>] [--listen <host:port> | --no-serve] [--name <cell>] [--no-tenders] [--exit]
|
|
76
|
+
foga quickstart [--dir <dir>] [--wallet <dir>] [--port <n>] [--listen <host:port> | --no-serve] [--name <cell>] [--no-tenders] [--exit]
|
|
68
77
|
```
|
|
69
78
|
|
|
70
|
-
`--
|
|
79
|
+
`--wallet` is the wallet to be born with (`~/.foga/wallet`; made when missing, never inside the quickstart directory), `--port` anvil's (8545; a port already answering JSON-RPC is reported, not reused), `--listen` the node's (`127.0.0.1:8787`), `--name` the cell's (`quick`), `--no-tenders` skips the two tenders, `--exit` stops everything once the summary is printed (the check CI runs). A second quickstart in the same directory replaces the first: the chain ended with its process and the old configuration points at nothing, so the directory is cleared and written again with the same wallet; the client configurations keep the same paths and need no change.
|
|
71
80
|
|
|
72
81
|
## Windows
|
|
73
82
|
|
|
@@ -75,4 +84,4 @@ Foundry publishes a Windows build: download the `win32_amd64` zip of a release f
|
|
|
75
84
|
|
|
76
85
|
## What it is not
|
|
77
86
|
|
|
78
|
-
A quickstart is a sandbox: a chain that ends with the process,
|
|
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).
|
package/docs/wallet.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# The wallet, signed calls, and becoming a cell (foga wallet, FOGA-EIP712, become-a-cell)
|
|
2
|
+
|
|
3
|
+
The principle (design memo, section 10): **the wallet is the first step, made by default, never a reason to stop; identity is one's own; only the genome replicates.** Every agent that touches FOGA — one that will be a cell, one that only calls other cells — starts by making its own keys on its own machine. Nothing in the install path requires it: a caller without a wallet keeps everything it has today (anonymous reads and builds, a Bearer key for acts), and a wallet is never made for someone else or sent anywhere. What a wallet adds is a credential that is the agent's own: a signature any FOGA cell can check, instead of a secret one cell issued.
|
|
4
|
+
|
|
5
|
+
## foga wallet new
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
foga wallet new # ~/.foga/wallet: owner.key, operator.key, guardian.key (0600 each) and wallet.json
|
|
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)
|
|
12
|
+
```
|
|
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.
|
|
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).
|
|
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.
|
|
19
|
+
|
|
20
|
+
## Signed calls: FOGA-EIP712
|
|
21
|
+
|
|
22
|
+
A third way in beside no credential and `Authorization: Bearer <key>`:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Authorization: FOGA-EIP712 <base64url(JSON envelope)>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The envelope is EIP-712 typed data signed by the caller's wallet, with the domain `{name: "FOGA", version: "1", chainId, verifyingContract: <the cell's account>}` — so a signature made for one cell means nothing to another — in one of two shapes:
|
|
29
|
+
|
|
30
|
+
| Shape | Message | Validity | Use |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| `Request` | `method`, `path` (with its query), `bodyHash` (keccak256 of the body; of nothing for a GET), `issuedAt`, `expiresAt`, `nonce` | 120 s at most; good once (the node keeps the nonce until it expires) | an act: whoever captures the header can replay nothing with it |
|
|
33
|
+
| `Session` | `issuedAt`, `expiresAt`, `nonce` | 600 s at most; presented as often as the session lasts | a client with static headers (an MCP client given `Authorization` once); the policy decides how far it reaches |
|
|
34
|
+
|
|
35
|
+
The envelope's JSON: `{"v": 1, "signer": "0x…", "agentId": "11255" (optional), "issuedAt": …, "expiresAt": …, "nonce": "0x…", "request": {"method", "path", "bodyHash"} (absent for a session), "signature": "0x…"}`. `foga wallet sign (--config <cell.config.json> | --url <its node's URL> | --cell <account> --chain-id <n>) [--dir <wallet>] [--role signer|operator|owner|guardian] [--ttl <s>] [--agent-id <id>] [--method POST --path /api/x --body '<json>']` prints a header for a cell — the operator names its configuration, anyone else the node's URL (`/healthz` says which account on which chain) or the two values by hand; in code (`src/http/signed.ts`), `signAuthorization(wallet, {chainId, cell, request?, ttlSeconds?, agentId?})` is the same thing with ethers, and any EIP-712 library reproduces it from the types above.
|
|
36
|
+
|
|
37
|
+
**What a signer is granted** is the cell's configuration, `access.signers`:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
"access": {
|
|
41
|
+
"keys": [ … ],
|
|
42
|
+
"signers": {
|
|
43
|
+
"default": "build",
|
|
44
|
+
"allow": [
|
|
45
|
+
{ "address": "0x…", "level": "act", "visibility": "caller", "note": "a partner's agent" },
|
|
46
|
+
{ "agentId": "42", "level": "act" }
|
|
47
|
+
],
|
|
48
|
+
"standing": { "minLevel": 2, "level": "act" },
|
|
49
|
+
"actsNeedRequestBinding": true
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
In order: the cell's own operator or owner signing remotely is granted act with the operator's view; an `allow` entry by address; an `allow` entry by ERC-8004 agent id, when the signer is that agent's owner or agent wallet on this world's registry (the envelope names the id, the node checks the registry); a signer whose KYA standing under the cell's scheme reaches `standing.minLevel`; otherwise `default` — `build` (what the public gets, but as itself: its decisions are recorded under its address), `read`, or `none` (signatures refused). With `actsNeedRequestBinding` (the default), a session envelope reaches `build` whatever the grant: an act wants an envelope bound to its request. A valid signature at `build` can pre-check, simulate and build calldata for its own account; nothing it says changes a gate.
|
|
55
|
+
|
|
56
|
+
The HTTP entrance tells a caller what it is (`GET /api/capabilities` → `caller.via`: `anonymous`, `key` or `signature`), and `/mcp` and `/a2a` take the same header. Over stdio there is nothing to sign: the local client holds the operator key or is the machine's user.
|
|
57
|
+
|
|
58
|
+
## become-a-cell
|
|
59
|
+
|
|
60
|
+
Every cell's MCP server carries the genome's instructions: the prompt `become-a-cell` and the resource `foga://docs/quickstart` (this repository's docs/quickstart.md). An agent that meets a cell — connects to its MCP entrance, as the public or with any credential — can read how to make itself one: the wallet first, then `npx -y @finchip.ai/foga quickstart` for a living cell on a local chain, then `foga cell new --wallet … && foga cell init --wallet …` on a public chain — and, with the wallet alone, how to call the cell it met as itself (`foga wallet sign --url <that node>`). What replicates is the program (the package, the catalog, the acceptance); the identity the new cell makes is its own and nothing of the first cell's is copied. The relation between cells is between peers on the chain — tenders minted and taken — not between a model and copies of it.
|
package/docs/world.md
CHANGED
|
@@ -48,3 +48,15 @@ It is tested with Foundry (`contracts/test/Identity8004Stub.t.sol`, no external
|
|
|
48
48
|
## Public chains
|
|
49
49
|
|
|
50
50
|
Sepolia runs are read-only in Phase 1 (doctor). Phase 2c deploys FOGA-owned components on Sepolia — eligibility modules, FOGA's own ACDF adapter and revision-4 escrow, per-cell accounts — with the same orchestrator, a gas multiplier (`--gas-multiplier 1.5`) and `--resume`, following the lessons of the upstream runs: take every gas limit from the live `eth_estimateGas`, never from a local chain.
|
|
51
|
+
|
|
52
|
+
## Verifying the sources (Sourcify)
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
npm run bundle:verify # rebuilds the bundle; leaves .foga-build/out/<repo>/build-info/
|
|
56
|
+
foga world verify --world deployments/sepolia.json --sourcify # submits every FOGA-deployed contract of the world
|
|
57
|
+
foga world verify --world deployments/local.json --sourcify --dry-run # writes what would be sent (a local chain is always a dry run)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A contract a world deployed from the bundle is verified on Sourcify (`src/world/sourcify.ts`, the 2c leftover the memo's section 9 lists) with the very compiler run that produced it: `forge build` leaves a build-info file per run beside the artifacts — the standard JSON input (sources at the locked commits, settings, remappings) and the output — and `npm run bundle:verify` (or `foga build`) writes them under `.foga-build/out/<repo>/build-info/`. For each `deployed` step of the world file, the request is the build-info of the step's repository whose output holds a contract of the bundle key's name with the bundle's runtime code, the identifier `<path solc saw>:<Name>`, the compiler's long version from the contract's own metadata (`0.8.28+commit.7893614a`), and the creation transaction the world file recorded. Nothing is recompiled, flattened or rewritten for the verifier: what Sourcify checks is what the bundle was built from, and a contract the build-info does not hold is reported `missing` with the reason (an upstream contract attached rather than deployed, a bundle rebuilt without `--build-info`).
|
|
61
|
+
|
|
62
|
+
The live path is Sourcify's v2 API: `GET /v2/contract/{chainId}/{address}` first (a contract already verified is `already`), then `POST /v2/verify/{chainId}/{address}` with `{stdJsonInput, compilerVersion, contractIdentifier, creationTransactionHash}`, and the ticket's `GET /v2/verify/{verificationId}` polled until `isJobCompleted` — `exact_match` or `match` (creation and runtime match named), `failed` with Sourcify's error, `error` when the request was refused or timed out. `--only <id,id>` narrows to some steps, `--server` another instance (`https://sourcify.dev/server` by default), `--out` where the requests and the report go (`.foga-build/sourcify/<world name>/`: one `<step>.json` per request and `report.json`, schema `foga.sourcify/1`, with a summary by outcome). Exit 3 when anything is `missing`, `failed` or `error`. `test/sourcify.test.ts` runs the matching, a dry run and the v2 exchange against a stand-in; CI dry-runs the whole local world (47 requests, 0 missing) after each bundle verification.
|
package/package.json
CHANGED
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.
|
|
11
|
+
"version": "0.6.0-alpha.7",
|
|
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.
|
|
17
|
+
"version": "0.6.0-alpha.7",
|
|
18
18
|
"runtimeHint": "npx",
|
|
19
19
|
"transport": {
|
|
20
20
|
"type": "stdio"
|