@forgezero/agent 0.1.39 → 0.1.41

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 CHANGED
@@ -1,399 +1,408 @@
1
+ <!--
2
+ GENERATED FILE — do not edit.
3
+
4
+ Change scripts/generate-guides.ts or its typed sources, run `bun run guides`,
5
+ and commit the generator and rendered files together.
6
+ -->
7
+
1
8
  # @forgezero/agent
2
9
 
3
- **`fz` controls the machine; `fz-agent` runs its managed work.**
10
+ The node agent deploys on a compute; tenant mode also holds one complete project across environments in RAM and serves it over a group-scoped unix socket, while platform mode never duplicates the API vault.
4
11
 
5
- The agent keeps a project-scoped copy of your vault in RAM and answers over a
6
- unix socket. `@forgezero/vault` prefers that socket over an API key whenever it
7
- exists, so an application on a machine running the agent reads its secrets
8
- because of *where it is*, not because it holds something that could be stolen
9
- from it.
12
+ ## Global package root and supported runtimes
10
13
 
11
- ```bash
12
- bun add -g @forgezero/agent # or let `fz agent install` do it
13
- fz --help
14
+ Every managed compute. It owns node identity, attestation and repository pipelines. Tenant mode additionally holds one whole project across its environments in RAM and answers over a unix socket; platform mode never replicates the API vault beside the process that already holds every unlocked realm seed. Supported runtimes: bun, node. The global base/root import is @forgezero/agent. Every public import or command is listed below; the documentation inventory is checked in both directions against package.json exports.
15
+
16
+ ```text
17
+ import * as root from '@forgezero/agent';
14
18
  ```
15
19
 
16
- The one public package installs both commands. Keeping bootstrap and the daemon
17
- in one version prevents a newly installed `fz` from provisioning a different
18
- agent protocol. Both executable artifacts bundle their ForgeZero runtime
19
- dependencies, so the constrained SSH bootstrap can transfer the reviewed pair
20
- to a new host without relying on a checkout, `node_modules`, or a second remote
21
- package-resolution step. Library subpath exports remain modular packages.
20
+ ## Commands
22
21
 
23
- The operator command starts browser-first platform genesis, inspects status,
24
- wraps processes, and installs the daemon. Genesis opens the one-use founder
25
- invitation, waits for the new passkey account to approve this CLI through device
26
- authorization, then continues custody in the browser. Passkey PRF output and
27
- both recovery phrases never enter the terminal:
22
+ bun add -g @forgezero/agent — Install the version-matched fz operator CLI and fz-agent daemon.
23
+ fz bootstrap platform --help Inspect attended platform bootstrap inputs before applying them.
24
+ fz bootstrap tenant --help Inspect tenant enrolment/bootstrap inputs.
25
+ fz status Read installed service and bootstrap evidence.
28
26
 
29
- ```bash
30
- fz keys
27
+ ```text
28
+ bun add -g @forgezero/agent
29
+ fz bootstrap platform --help
30
+ fz bootstrap tenant --help
31
31
  fz status
32
- fz genesis --mode 2-of-3 --api https://api.example --app https://console.example
33
- fz unlock --phrase-file /secure/offline-phrase.txt # run once per participating custodian
34
- ```
35
-
36
- Install the service explicitly:
37
-
38
- ```bash
39
- fz agent install --apply # writes a hardened systemd unit
40
- ```
41
-
42
- ## Host bootstrap
43
-
44
- The package is also the host installer. There is no separate shell setup
45
- script and no second implementation for tenant or platform machines:
46
-
47
- ```bash
48
- fz bootstrap platform # interactive plan, no mutation
49
- sudo fz bootstrap platform prepare --bootstrap-config ./platform.json --apply
50
- # Register the printed read-only deploy key. For genesis, run --apply on all
51
- # three Agency members concurrently; later agency:none joiners run alone.
52
- sudo fz bootstrap platform --bootstrap-config ./platform.json --apply
53
- sudo fz bootstrap tenant --bootstrap-config ./tenant.json --apply
54
- sudo fz bootstrap metal --bootstrap-config ./metal.json --apply
55
- sudo fz bootstrap status
56
- sudo fz bootstrap repair --bootstrap-config ./original.json --apply
57
- ```
58
-
59
- Platform database participation is explicit in the private JSON configuration.
60
- The three genesis nodes use `database.agency: "member"`; later DB+API joiners
61
- normally use `database.agency: "none"`, which starts a Coordinator and DBServer
62
- without adding a fourth Agency member. Enrolment is likewise explicit:
63
- `enrolment.source: "genesis-derived"` only for the offline genesis fleet, or
64
- `"api-token"` with `tokenFile` for every API-authorized later node. No behavior
65
- is inferred from a hostname or numeric suffix.
66
-
67
- Repair is not a second installation path: the CLI detects the installed
68
- platform/tenant or metal state and dispatches to that profile's validator. It
69
- fails closed if both state formats are present or neither is present. A private
70
- intent journal binds an interrupted first apply, and later repair refuses a
71
- config whose immutable host identity differs from the installed profile. Status checks the persisted
72
- identity record, both Agent sockets, profile-owned systemd units and, for a
73
- platform host, the active API health endpoint, nginx configuration and live
74
- Coordinator-mode evidence.
75
-
76
- Platform bootstrap supports an elastic Community ArangoDB 3.11.14 fleet. The
77
- first three database-capable computes establish the writable cluster; later
78
- database joiners and API-only computes use the same command with their typed
79
- profile and an API-issued one-time enrolment file. Nothing treats three as a
80
- maximum.
81
-
82
- After genesis, a tenant can designate an enrolled compute as a bootstrap
83
- runner. The API creates a project-bound, expiring job containing only public
84
- SSH coordinates and a pinned Ed25519 host-key fingerprint. The runner claims it
85
- with its hybrid node identity, reads its SSH key only from a local systemd
86
- credential, pins one vetted DNS answer, transfers this packaged CLI, and runs
87
- the same `fz bootstrap tenant` flow on the target. Claim renewal, completion,
88
- retry and cancellation are fenced in the compute aggregate; the API never
89
- stores the SSH private key or a plaintext enrolment token.
90
-
91
- The attended Cloudflare resource phase is part of the same published command,
92
- but remains separate from the root host install so its management token never
93
- enters the API or Agent service environment:
94
-
95
- ```bash
96
- chmod 600 cloudflare-bootstrap.json cloudflare-management.token
97
- fz bootstrap platform cloudflare --bootstrap-config ./cloudflare-bootstrap.json
98
- fz bootstrap platform cloudflare --bootstrap-config ./cloudflare-bootstrap.json --apply
99
- # After every host is bootstrapped and publishing healthy KV state:
100
- fz bootstrap platform cloudflare verify --bootstrap-config ./cloudflare-handoff.json
101
- ```
102
-
103
- The reviewed JSON has `format: 1`, kind
104
- `forgezero-cloudflare-bootstrap-request`, a `checkpointPath`, the typed
105
- `coordinates`, and `tokenFiles` containing file paths only. `coordinates.nodes`
106
- explicitly lists each node name, origin hostname, loopback service, Tunnel name
107
- and Access application name. Relative token, checkpoint and Worker project
108
- paths resolve beside the config file. Plan is offline. Apply creates/reuses one
109
- shared KV namespace, Worker and Access service token plus one Tunnel, Access
110
- application, ingress rule and DNS record per node. The command prints only
111
- resource IDs and per-node handoff paths. The complete owner-only checkpoint
112
- stays on the operator laptop; `<checkpoint>.hosts/<node>.json` contains only
113
- that node's connector and required runtime capabilities and is the file copied
114
- to the host for `fz bootstrap platform`. Management tokens are never persisted.
115
- Each host pins cloudflared diagnostics to loopback and bootstrap/status requires
116
- the expected Tunnel UUID, a connector UUID, and all four documented edge
117
- connections—not merely an active process. The final `verify` is read-only: it
118
- uses the checkpoint's Access service credential only on the operator laptop to
119
- probe every origin, then probes every stable Worker domain without privileged
120
- headers. Its printed evidence contains hostnames and HTTP status codes only.
32
+ ```
121
33
 
122
- ## What changes for an application
34
+ ## fz
123
35
 
124
- Nothing.
36
+ The operator command: keys, status, genesis, unlock, run, and agent install. This entry requires a Bun/Node host runtime.
125
37
 
126
- ```ts
127
- import { ForgeZero } from '@forgezero/vault';
128
- const fz = new ForgeZero({ project: 'altpilot', environment: 'production' });
129
- await fz.get('STRIPE_KEY');
38
+ ```text
39
+ fz --help
130
40
  ```
131
41
 
132
- With no agent, that needs `FORGEZERO_API_KEY` in the environment. With the
133
- agent, it does not — and the key that would have been in the environment does
134
- not exist on the box.
42
+ ## fz run
135
43
 
136
- ## A local copy, not a cache
44
+ The fallback for software that cannot read the local socket: start a process with scoped vault values in its environment. This entry requires a Bun/Node host runtime.
137
45
 
138
- The agent holds the **whole assigned project across every environment**, not
139
- just what has already been read. Each application still selects one environment
140
- and cannot cross the enrolled project boundary. That is the difference between surviving an outage and not: a
141
- cache only has what you fetched, so a secret you have never read is exactly the
142
- one you cannot get when the platform is unreachable.
46
+ ```text
47
+ fz run --help
48
+ ```
143
49
 
144
- The cost, stated rather than buried: a compromised guest exposes everything in
145
- scope rather than only what was read. It is bounded by the scope the compute is
146
- assigned to, and by SEV-SNP keeping the host out of guest memory.
50
+ ## fz agent install
147
51
 
148
- **Never on disk.** A cache file would hand an attacker with filesystem access
149
- every secret this guest has ever held — the exact artefact the design exists to
150
- remove. A restart re-fetches.
52
+ Provision the same hardened agent service locally that the platform provisions remotely. This entry requires a Bun/Node host runtime.
151
53
 
152
- **Invalidated by cursor, not by a timer.** A TTL alone means a rotated secret
153
- keeps working for the length of the TTL, which is the window rotation exists to
154
- close. The agent polls a change cursor and drops what moved.
54
+ ```text
55
+ fz agent install --help
56
+ ```
155
57
 
156
- **A stale read is refused, not served.** If sync has not succeeded within the
157
- staleness bound, `get` fails rather than returning a value it can no longer
158
- vouch for. An application that receives a revoked credential and succeeds with
159
- it is worse off than one that receives an error — the error is visible.
58
+ ## fz-agent
160
59
 
161
- ## Two postures, decided by the hardware
60
+ The service entry point. Identity and private Git material arrive only as systemd credentials. A platform bootstrap may use a statically configured local control socket; an enrolled tenant guest instead builds each deployment manager from the signed server claim and reads all commands from the checked-out .fz definition. Tenant cache scope and live attestation still need to be constructed from enrolment. This entry requires a Bun/Node host runtime.
61
+
62
+ ```text
63
+ fz-agent --help
64
+ ```
65
+
66
+ ## fz-agent (socket)
67
+
68
+ identity, sign, attest, get, sync and held — the whole interface an application sees. This entry requires a Bun/Node host runtime.
69
+
70
+ ```text
71
+ fz-agent (socket) --help
72
+ ```
73
+
74
+ ## @forgezero/agent/migration-pull
75
+
76
+ PQ-authenticated outbound lifecycle claims with fenced renewal, awaited async execution, idempotent acknowledgement and bounded shutdown drain. The executor reports local facts only.
77
+
78
+ ```text
79
+ import * as api from '@forgezero/agent/migration-pull';
80
+ ```
81
+
82
+ ## @forgezero/agent/lifecycle-helper
83
+
84
+ The root-owned fixed-operation boundary for migration work. Signed controller claims can request only private reachability probes, local health checks, or stops of units named in a root-owned profile; they cannot carry commands.
85
+
86
+ ```text
87
+ import * as api from '@forgezero/agent/lifecycle-helper';
88
+ ```
89
+
90
+ ## @forgezero/agent/provision
91
+
92
+ The install plan as data, with no transport — so `fz` running locally and the platform running over SSH provision an identical machine.
93
+
94
+ ```text
95
+ import * as api from '@forgezero/agent/provision';
96
+ ```
97
+
98
+ ## @forgezero/agent/subscribe
99
+
100
+ Realtime scope sync over a WebSocket, with the poll kept underneath it: the poll is what establishes freshness, and a change it finds that the socket never sent is proof the socket is lying.
101
+
102
+ ```text
103
+ import * as api from '@forgezero/agent/subscribe';
104
+ ```
105
+
106
+ ## @forgezero/agent/ssh-listen
107
+
108
+ The socket SSH_AUTH_SOCK points at. The protocol handler had nowhere to speak — an agent protocol with no socket is a parser. The containing directory is 0700 before anything binds, because the socket’s own mode is only corrected after it already exists.
109
+
110
+ ```text
111
+ import * as api from '@forgezero/agent/ssh-listen';
112
+ ```
113
+
114
+ ## @forgezero/agent/ssh-server
162
115
 
163
- | | |
164
- |---|---|
165
- | `attested` | SEV-SNP guest. The credential is a hardware report, and the platform can refuse a node whose measurement is wrong. |
166
- | `enrolled` | No SNP. The credential is the enrolment token plus a hybrid Ed25519 + ML-DSA-65 signature. |
116
+ Serve the SSH agent protocol so SSH_AUTH_SOCK points at the vault — two operations implemented, and ADD_IDENTITY refused so no process can inject a key ssh would offer to every host.
167
117
 
168
- Both are real. `enrolled` is still strictly better than an API key in the
169
- application: the key never leaves the agent, rotation reaches every process, and
170
- the socket is filesystem-scoped. The socket is mode `0660` under the dedicated
171
- `forgezero-vault` group; add only the intended application service account to
172
- that group. The credential-free deployment runner is not a member.
118
+ ```text
119
+ import * as api from '@forgezero/agent/ssh-server';
120
+ ```
121
+
122
+ ## @forgezero/agent/pipeline
123
+
124
+ CI/CD on the compute: secrets reach the step that named them and nothing else, attestation gates the run, and a required attestation that fails is a refusal rather than a downgrade.
125
+
126
+ ```text
127
+ import * as api from '@forgezero/agent/pipeline';
128
+ ```
129
+
130
+ ## @forgezero/agent/agent-heartbeat
131
+
132
+ Signed node health publication with bounded renewal and shutdown withdrawal.
133
+
134
+ ```text
135
+ import * as api from '@forgezero/agent/agent-heartbeat';
136
+ ```
137
+
138
+ ## @forgezero/agent/agent-update
139
+
140
+ Verified Agent update planning against the platform release coordinate.
141
+
142
+ ```text
143
+ import * as api from '@forgezero/agent/agent-update';
144
+ ```
145
+
146
+ ## @forgezero/agent/agent-update-helper
147
+
148
+ Root-owned fixed update operations with registry origin and integrity validation.
149
+
150
+ ```text
151
+ import * as api from '@forgezero/agent/agent-update-helper';
152
+ ```
153
+
154
+ ## @forgezero/agent/bootstrap
155
+
156
+ Typed platform and tenant bootstrap planning, application and status contracts.
157
+
158
+ ```text
159
+ import * as api from '@forgezero/agent/bootstrap';
160
+ ```
173
161
 
174
- The attested posture is the intended production upgrade, not current live
175
- evidence: report acquisition and AMD-rooted measurement verification still have
176
- to cross the production fleet. Until then the node is honestly `enrolled`; the
177
- Agent never manufactures an attestation-shaped fallback.
162
+ ## @forgezero/agent/capacity-calibration
178
163
 
179
- Every Agent request uses the same hybrid signature codec as an external API key
180
- and signs its one-use hybrid ML-KEM-768 + X25519 response key. Vault replication,
181
- deployment claims, attestation and metal provisioning responses are AES-256-GCM
182
- sealed to that request before crossing the edge; there is no Ed25519-only or
183
- successful plaintext-response downgrade.
164
+ One-time target-local safe-concurrency calibration with private evidence.
184
165
 
185
- The first guest enrolment uses the same contract before the identity exists in
186
- the database: the guest signs the exact token and public-key body with both keys
187
- it is asking the API to admit, and it accepts only an ML-KEM-sealed
188
- acknowledgement. The short-lived token is therefore not an unsigned Agent path.
166
+ ```text
167
+ import * as api from '@forgezero/agent/capacity-calibration';
168
+ ```
169
+
170
+ ## @forgezero/agent/cloudflare-bootstrap
189
171
 
190
- ## The socket interface
172
+ Attended Cloudflare Tunnel, KV and runtime-token bootstrap with private resumable handoff.
191
173
 
174
+ ```text
175
+ import * as api from '@forgezero/agent/cloudflare-bootstrap';
192
176
  ```
193
- get one secret
194
- sync what changed since a cursor
195
- held what this guest is holding, by name only
177
+
178
+ ## @forgezero/agent/cloudflare-edge
179
+
180
+ Strict Cloudflare REST operations used by the attended bootstrap controller.
181
+
182
+ ```text
183
+ import * as api from '@forgezero/agent/cloudflare-edge';
196
184
  ```
197
185
 
198
- The application Vault socket never accepts identity, signing, attestation,
199
- commands or deployment definitions. The Agent signs and attests only inside its
200
- own outbound clients. Otherwise a compromised application could ask for a valid
201
- node signature over a deployment or whole-project sync request and impersonate
202
- the Agent without ever extracting its key. Deployment enters through the
203
- root-only control socket or signed outbound claim path, then the deployment
204
- manager validates the checked-in definition and submits it to the common keyed
205
- queue.
186
+ ## @forgezero/agent/compute
187
+
188
+ Compute claim, renewal, execution and completion client contracts.
206
189
 
207
- ## Project context for any repository
190
+ ```text
191
+ import * as api from '@forgezero/agent/compute';
192
+ ```
193
+
194
+ ## @forgezero/agent/credential-schema
195
+
196
+ Canonical systemd credential names and validation shared by bootstrap and services.
197
+
198
+ ```text
199
+ import * as api from '@forgezero/agent/credential-schema';
200
+ ```
208
201
 
209
- Conversation memory is not a project database. Initialize one vendor-neutral,
210
- Git-owned context and generate the small files each AI product discovers:
202
+ ## @forgezero/agent/definition
211
203
 
212
- ```bash
204
+ Validated project deployment definition and workload profiles.
205
+
206
+ ```text
207
+ import * as api from '@forgezero/agent/definition';
208
+ ```
209
+
210
+ ## @forgezero/agent/deploy-file
211
+
212
+ Read and validate a checked-out .fz deployment file without executing project input.
213
+
214
+ ```text
215
+ import * as api from '@forgezero/agent/deploy-file';
216
+ ```
217
+
218
+ ## @forgezero/agent/metal-bootstrap
219
+
220
+ Physical-metal bootstrap planning, application and status evidence.
221
+
222
+ ```text
223
+ import * as api from '@forgezero/agent/metal-bootstrap';
224
+ ```
225
+
226
+ ## @forgezero/agent/metal-provision
227
+
228
+ Validated confidential guest provisioning profiles and cloud-init rendering.
229
+
230
+ ```text
231
+ import * as api from '@forgezero/agent/metal-provision';
232
+ ```
233
+
234
+ ## @forgezero/agent/platform-bootstrap-runtime
235
+
236
+ Pure platform bootstrap validation, render and resumable phase-state contracts.
237
+
238
+ ```text
239
+ import * as api from '@forgezero/agent/platform-bootstrap-runtime';
240
+ ```
241
+
242
+ ## @forgezero/agent/project-context
243
+
244
+ Project identity and repository context validation for operator and managed runs.
245
+
246
+ ```text
247
+ import * as api from '@forgezero/agent/project-context';
248
+ ```
249
+
250
+ ## @forgezero/agent/provisioning-pull
251
+
252
+ Signed provisioning work claim, renewal, execution and acknowledgement.
253
+
254
+ ```text
255
+ import * as api from '@forgezero/agent/provisioning-pull';
256
+ ```
257
+
258
+ ## @forgezero/agent/schema/deploy-v2.json
259
+
260
+ Published JSON Schema for deployment definition version 2.
261
+
262
+ ```text
263
+ import * as api from '@forgezero/agent/schema/deploy-v2.json';
264
+ ```
265
+
266
+ ## @forgezero/agent/software
267
+
268
+ Exact managed-software inventory, requirement and evidence contracts.
269
+
270
+ ```text
271
+ import * as api from '@forgezero/agent/software';
272
+ ```
273
+
274
+ ## @forgezero/agent/software-helper
275
+
276
+ Root-owned fixed installation/update boundary for declared software.
277
+
278
+ ```text
279
+ import * as api from '@forgezero/agent/software-helper';
280
+ ```
281
+
282
+ ## @forgezero/agent/ubuntu
283
+
284
+ Ubuntu host validation and deterministic systemd/unit rendering helpers.
285
+
286
+ ```text
287
+ import * as api from '@forgezero/agent/ubuntu';
288
+ ```
289
+
290
+ ## fz-agent (replica)
291
+
292
+ The project-scoped vault replica: resident in RAM, never on disk, invalidated by cursor rather than by timer, and refusing a read it can no longer vouch for.
293
+
294
+ ```text
295
+ fz-agent (replica) --help
296
+ ```
297
+
298
+ ## Outbound control boundary
299
+
300
+ The Metal Agent initiates outbound signed HTTPS for orchestration. Applications use the project-scoped local Unix socket; the platform never opens an inbound shell control plane.
301
+
302
+ ```text
303
+ metal/compute Agent -> outbound signed HTTPS -> platform API
304
+ application -> local Unix socket -> project-scoped Vault replica
305
+ ```
306
+
307
+ ## Keep project truth synchronized
308
+
309
+ One versioned ForgeZero manifest generates thin AI-tool adapters and CI rejects drift.
310
+
311
+ ```text
213
312
  fz project init
214
- # edit .forgezero/project.json and create its named truth sources
215
313
  fz project sync
216
314
  fz project check
217
315
  ```
218
316
 
219
- `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, Copilot instructions and the Cursor rule
220
- are generated adapters. They contain no independent architecture or progress.
221
- `fz project check` fails when a truth source is missing or an adapter was edited
222
- by hand. Tools and skills remain optional execution aids; accepted decisions and
223
- status live in the repository and therefore survive switching AI agents.
317
+ ## Install both machine commands
318
+
319
+ One public package installs `fz` and `fz-agent` at the same version. `fz` breaks the bootstrap circle from an operator machine; `fz-agent` runs managed work. Keeping them together prevents a new bootstrap command from provisioning an old daemon protocol.
320
+
321
+ ```text
322
+ bun add -g @forgezero/agent
323
+ fz --help
324
+ fz-agent --help
325
+ ```
224
326
 
225
- ## Browser-authorized project control
327
+ ## One project memory for every AI tool
226
328
 
227
- Sign in from any checkout with the public CLI. The terminal displays a device
228
- code and the browser approves it in the selected tenant realm:
329
+ Conversation memory and dated audits are observations, not architecture. `fz project init` creates one strict, vendor-neutral manifest in Git; `sync` renders only the thin discovery files Claude, Codex, Gemini, Copilot and Cursor expect, and `check` fails if a named truth source disappeared or an adapter was edited independently. Teams can switch tools without restating the project and without maintaining five competing plans.
229
330
 
230
- ```bash
231
- fz login --api https://api.forgezero.net --realm acme
232
- fz whoami
233
- fz ui routes --json
331
+ ```text
332
+ fz project init # creates .forgezero/project.json
333
+ # edit the manifest and its named truth sources
334
+ fz project sync # generate vendor adapters
335
+ fz project check # CI/handoff drift gate
234
336
  ```
235
337
 
236
- The short session is stored by API origin and realm in an owner-only local file.
237
- It is never committed to `.fz/config.json`. A protected mutation opens a browser
238
- page for passkey or TOTP proof and then replays the exact request once; the CLI
239
- has no privileged server mode.
338
+ ## Genesis is browser-first; recovery can remain headless
339
+
340
+ The CLI starts device authorization before the founder exists and opens the one-use invitation created by `fz bootstrap platform`. The browser creates the account, registers and freshly proves its passkey, confirms the account recovery phrase, then explicitly approves the displayed terminal code and continues to custody. The CLI receives only a bounded session. Custody phrases and passkey PRF output remain in the browser. After restart, each participating custodian opens only their own phrase envelope using an owner-only phrase file or stdin and contributes the share from their own authenticated, freshly proved session. No CLI process collects several custodians’ phrases. SSH remains transport and Git interoperability only; it is not a ForgeZero authentication or custody factor. Neither path creates a privileged server bypass.
341
+
342
+ ```text
343
+ fz genesis --mode 2-of-3 --api https://api.example --app https://console.example
344
+ fz status
345
+ fz unlock --phrase-file /secure/offline-phrase.txt
346
+ ```
240
347
 
241
- Any ordinary website operation is available to developers and AI agents through
242
- the same API and access matrix:
348
+ ## Use website operations from a project or an AI agent
243
349
 
244
- ```bash
350
+ Every UI operation is an API operation. `fz ui routes --json` discovers the exact actions allowed by the signed-in account’s current realm, lifecycle stage and grants from the API’s enforced matrix, so the CLI and AI agents carry no copied route list. `fz api` (also `fz ui`) executes authenticated JSON GET, POST, PUT, PATCH and DELETE without requiring a browser visit for ordinary work. Paths are forced onto the signed-in origin, query values are repeatable, bodies can be inline, stdin or a file, and server grants plus fresh-proof rules remain authoritative. `fz project init|sync|check` separately gives AI tools one Git-persisted project memory.
351
+
352
+ ```text
353
+ fz ui routes --json
245
354
  fz ui routes pipeline
246
355
  fz ui get /workspace/pipelines --query projectKey=my-project
247
- fz api post /workspace/pipelines/status \
248
- --data '{"pipelineKey":"pl_…","enabled":false}'
356
+ fz api post /workspace/pipelines/status --data '{"pipelineKey":"pl_…","enabled":false}'
249
357
  generate-request | fz api post /workspace/action --data -
250
358
  ```
251
359
 
252
- Only realm-relative paths are accepted, so the saved session cannot be forwarded
253
- to another origin. Inline JSON, stdin and JSON files are supported. Use
254
- `fz logout` to revoke the remote session and remove the local copy.
255
-
256
- `fz ui routes --json` reads the action catalog from the API's enforced access
257
- matrix. It lists only the current account's current realm, stage and grants, so
258
- developers and AI agents can discover the available control surface without a
259
- copied CLI command list or a visit to the website. Execute any listed action
260
- with `fz ui <method> <path>`; sensitive actions open the same fresh-proof flow
261
- as the website and replay the exact request once.
262
-
263
- ## Deployment definitions
264
-
265
- A repository may commit `.fz/deploy.json` with its own profiles, prerequisite
266
- checks, commands, and the exact secret names each step needs. ForgeZero does not
267
- choose a tenant's database, framework, or deploy shape. The agent validates the
268
- file before executing any command, prepares only the selected profile, and gives a
269
- step only the vault values it explicitly names. `await` returns the complete
270
- pipeline result; no polling service or persistent queue is required.
271
-
272
- Create and validate that file with the same public package that executes it:
273
-
274
- ```bash
275
- fz deploy catalog --channel production
276
- fz deploy init --profile app --software bun@1.3.14
277
- # Replace the explicit safe blockers with this project's release and health commands.
278
- fz deploy check
279
- fz deploy sync
280
- ```
281
-
282
- After sign-in, create and operate the durable pipeline from the same checkout.
283
- The project defaults to `.fz/config.json`; compute/profile attachments remain
284
- control-plane state rather than fields in the deploy file:
285
-
286
- ```bash
287
- fz deploy connect --provider github --repository acme/web \
288
- --clone-url https://github.com/acme/web.git --auth public \
289
- --target compute-eu@api --target compute-us@api
290
- fz deploy list
291
- fz deploy runs --pipeline <pipeline-key>
292
- fz deploy release --pipeline <pipeline-key> \
293
- --revision <full-40-character-commit> --wait
294
- ```
295
-
296
- `init` refuses to invent a generic release or health check: both generated steps
297
- exit non-zero until the project replaces them. `check` validates the published
298
- v2 schema and active software coordinates, then prints a formatting-independent
299
- semantic SHA-256 digest. The schema ships at
300
- `@forgezero/agent/schema/deploy-v2.json` and is served from
301
- `https://www.forgezero.net/schemas/deploy-v2.json`.
302
-
303
- Git is the only local-to-live synchronization mechanism. `sync` validates and
304
- prints that rule; it does not create a second mutable command copy in the API.
305
- The verified webhook identifies one exact commit, and a successful Agent result
306
- persists the definition digest so the Applications screen can prove which local
307
- contract became live.
308
-
309
- Catalog status is an admission boundary, not a suggestion. `testing` coordinates
310
- are visible in the development catalog for ForgeZero qualification but cannot be
311
- selected by any deploy file. A repository cannot promote software by calling
312
- itself development; only a reviewed catalog change to `active` unlocks it.
313
-
314
- The daemon owns source checkout and command execution. Bootstrap explicitly
315
- awaits release one because the API does not exist yet, then the Agent consumes a
316
- one-use platform enrolment capability. There is no branch watcher. Every normal
317
- platform or tenant release is one durable API delivery atomically expanded to
318
- one row for every independently attached compute target. Release-scoped steps
319
- run on one target elected by stable target ordering, never on a user-declared
320
- coordinator:
321
- `pending` is written before dispatch, `running` and a fenced lease before project
322
- code, and only an awaited successful pipeline writes `deployed`. An expired
323
- claim can be recovered; its stale token cannot renew or finish.
324
-
325
- Compute replacement uses the same outbound pattern through
326
- `@forgezero/agent/migration-pull`: claim one action for this exact compute,
327
- renew its fencing lease while the awaited function runs, and acknowledge the
328
- result before accepting more work. The public function accepts the executor as
329
- an async callback so an embedding Agent can keep privilege and OS policy outside
330
- the transport. Node evidence is deliberately local-only; it cannot claim
331
- Agency quorum, replication completion, seed residency, edge exclusion or
332
- Cloudflare route existence on behalf of the controller.
333
-
334
- The managed daemon uses `@forgezero/agent/lifecycle-helper` as that executor.
335
- It is a separate root service behind a group-scoped Unix socket and accepts only
336
- fixed lifecycle operations. Unit names and loopback health endpoints come from
337
- a root-owned profile; a signed claim cannot supply a command or path. For a
338
- cross-network database member, the common installer enrolls the headless WARP
339
- client from systemd credentials materialized only in `/run`. Readiness requires
340
- WARP to be connected and the controller-selected private peer IP and database
341
- port to be reachable. Database traffic is never exposed through a public IP,
342
- public hostname, or inbound firewall rule.
343
-
344
- The same package also runs the identity-only Metal Agent. It initiates outbound
345
- hybrid-signed HTTPS and requires request-bound PQ-sealed responses while pulling
346
- only claims assigned to its enrolled hostname, then passes
347
- the fixed claim over a local Unix socket to a narrowly privileged root helper.
348
- The helper can materialize the audited QEMU profile; it cannot clone a project,
349
- read Vault data, accept arbitrary commands, or retain tenant credentials. Root
350
- SSH is an operator-only platform recovery path, not the normal tenant
351
- dispatch mechanism.
352
-
353
- New guests also call this same public `fz agent install --apply --enrol` path
354
- from cloud-init. Metal provisioning does not carry a second handwritten Agent
355
- unit or deployment runner, so platform and tenant guests cannot drift at that
356
- foundation boundary.
357
-
358
- Shutdown has one process-wide deadline. New deployment claims and background
359
- sync stop first, claimed work drains under its lease fence, and only then do the
360
- queues and application socket close. Vault/attestation calls or a stale local
361
- socket cannot consume systemd's longer stop timeout: exceeding the Agent deadline
362
- is reported and exits non-zero instead of being silently killed midway by PID 1.
363
-
364
- Repository read authorization is explicit per pipeline: public HTTPS, the
365
- compute's systemd-sealed SSH deploy key, or a fine-grained HTTPS token selected
366
- by a project-vault secret name. A signed claim contains the mode and secret name
367
- only. The agent resolves the value from its scoped memory cache, limits the Git
368
- header to the repository origin, never writes the token into a command, and
369
- never exposes it to tenant pipeline steps. These provider-neutral modes are the
370
- complete contract; a GitHub App is not a missing dependency and would be added
371
- later only as an explicit provider-specific mode with expiry and revocation tests.
372
-
373
- An operator may also force a deployment and await the complete result over the
374
- private control socket:
375
-
376
- ```bash
377
- fz-agent deploy --revision=<full-40-character-commit> --release-executor
378
- fz-agent status
379
- fz-agent pause
380
- fz-agent pause-key --key=project:production
381
- fz-agent stop-key --key=project:production # running finishes; pending work is removed
382
- fz-agent start-key --key=project:production
383
- fz-agent cancel --id=q_42
384
- ```
385
-
386
- The node seed and read-only Git private key are loaded with
387
- `LoadCredentialEncrypted=`. Their encrypted, host-bound blobs persist under
388
- `/etc/forgezero/creds`; decrypted values exist only in the service's private
389
- `$CREDENTIALS_DIRECTORY`. Platform bootstrap has no second clone/build path.
390
- Bootstrap records the forge's SSH host key during the attended access check;
391
- the daemon uses `StrictHostKeyChecking=yes` and never accepts a new host key by
392
- itself.
393
-
394
- Full documentation: **https://www.forgezero.net/docs/agent**
395
-
396
- ## Licence
397
-
398
- MIT. Part of [ForgeZero](https://www.forgezero.net) — secrets, attested compute and
399
- deploys.
360
+ ## What changes for an application
361
+
362
+ Nothing. With no agent, this needs FORGEZERO_API_KEY in the environment. With the agent, it does not — and the key that would have been on the box does not exist. `@forgezero/vault` prefers the socket automatically.
363
+
364
+ ```text
365
+ import { ForgeZero } from '@forgezero/vault';
366
+
367
+ const fz = new ForgeZero({ project: 'altpilot', environment: 'production' });
368
+ await fz.get('STRIPE_KEY');
369
+ ```
370
+
371
+ ## A local copy, not a cache
372
+
373
+ The agent holds the whole assigned project across all of its environments, not just what has already been read. Each application still supplies its environment and cannot cross the enrolled project boundary. That is the difference between surviving an outage and not: a read-through cache lacks the never-before-read value precisely when the platform is unreachable. The cost is that a compromised guest exposes everything in the enrolled project rather than only what was read — bounded by the project and by SEV-SNP keeping the host out of guest memory.
374
+
375
+ ## Local access is explicit Unix membership
376
+
377
+ The socket is mode 0660 in the dedicated forgezero-vault group. A service account must be deliberately added to that group before it can read the project replica; unrelated local users and the credential-free deployment runner cannot connect. Mode 0600 would make the advertised app integration unusable, while a world-readable socket would hand the vault to every process on the guest.
378
+
379
+ ## Never on disk
380
+
381
+ A cache file would hand an attacker with filesystem access every secret this guest has ever held, which is precisely the artefact this design exists to remove. A restart re-fetches. There is no version of "just persist it, it is encrypted" that survives the key also being on the box.
382
+
383
+ ## Invalidated by cursor, and refused when stale
384
+
385
+ A TTL alone means a rotated secret keeps working for the length of the TTL the window rotation exists to close. The agent polls a change cursor and drops what moved, so a rotation propagates regardless of TTL. And if sync has not succeeded within the staleness bound, a read is refused rather than served: an application that receives a revoked credential and succeeds with it is worse off than one that receives an error, because the error is visible.
386
+
387
+ ## Two postures, decided by the hardware
388
+
389
+ An SNP guest can produce an attestation report and the platform can refuse a node whose measurement is wrong. Anything else authenticates with its enrolment token and a hybrid Ed25519 + ML-DSA-65 signature. Both are real: enrolled is still strictly better than an API key in the application, because the key never leaves the agent, rotation reaches every process, and the socket is filesystem-scoped.
390
+
391
+ ```text
392
+ attested /dev/sev-guest present -> hardware report
393
+ enrolled no SNP device -> enrolment token + hybrid signature
394
+ ```
395
+
396
+ ## The app socket cannot impersonate the node
397
+
398
+ The group-scoped application socket exposes only replica get, held and sync operations. Node identity, hybrid signing and attestation stay inside the Agent's outbound clients. Every Agent call requires HTTPS outside loopback, signs a one-use hybrid ML-KEM-768 + X25519 recipient and refuses a successful response that is not sealed back to that request. A process allowed to read one environment therefore cannot ask the Agent to sign a deployment, heartbeat or whole-project replication request and act as the node.
399
+
400
+ ```text
401
+ get one secret in the selected environment
402
+ sync invalidate rotated values
403
+ held names held in the selected environment
404
+
405
+ identity/sign/attest refused on the application socket
406
+ ```
407
+
408
+ Full rendered documentation: https://www.forgezero.net/docs/agent