@forgezero/agent 0.1.40 → 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 +357 -421
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.js +1 -1
- package/dist/fz-agent.js +1519 -3271
- package/dist/fz.js +1728 -3494
- package/dist/metal-bootstrap.js +1 -1
- package/dist/provision.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,472 +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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
```
|
|
27
|
+
```text
|
|
28
|
+
bun add -g @forgezero/agent
|
|
29
|
+
fz bootstrap platform --help
|
|
30
|
+
fz bootstrap tenant --help
|
|
30
31
|
fz status
|
|
31
|
-
|
|
32
|
-
fz unlock --phrase-file /secure/offline-phrase.txt # run once per participating custodian
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Install the service explicitly:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
fz agent install --apply # writes a hardened systemd unit
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Host bootstrap
|
|
42
|
-
|
|
43
|
-
The package is also the host installer. There is no separate shell setup
|
|
44
|
-
script and no second implementation for tenant or platform machines:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
fz bootstrap platform # interactive plan, no mutation
|
|
48
|
-
sudo fz bootstrap platform prepare --bootstrap-config ./platform.json --apply
|
|
49
|
-
# Register the printed read-only deploy key. For genesis, run --apply on all
|
|
50
|
-
# three Agency members concurrently; later agency:none joiners run alone.
|
|
51
|
-
sudo fz bootstrap platform --bootstrap-config ./platform.json --apply
|
|
52
|
-
sudo fz bootstrap tenant --bootstrap-config ./tenant.json --apply
|
|
53
|
-
sudo fz bootstrap metal --bootstrap-config ./metal.json --apply
|
|
54
|
-
sudo fz bootstrap status
|
|
55
|
-
sudo fz bootstrap repair --bootstrap-config ./original.json --apply
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Interactive bootstrap prompts only for missing typed inputs and validates every
|
|
59
|
-
answer before it changes the host. It must then render a secret-free plan, apply
|
|
60
|
-
idempotently, and verify the external and local result. Initial platform bring-up
|
|
61
|
-
has exactly three attended owner gates:
|
|
62
|
-
|
|
63
|
-
1. SMTP host, port, sender, optional user, and an owner-only password file; the
|
|
64
|
-
resulting provider must pass its configured health/send acceptance.
|
|
65
|
-
2. The separate Cloudflare phase consumes owner-only management and KV-runtime
|
|
66
|
-
token files, reconciles Tunnel/hostname state, and emits the node handoff; the
|
|
67
|
-
normal API/Agent never receives the management token.
|
|
68
|
-
3. `prepare` prints that machine's read-only Git deploy public key. The owner adds
|
|
69
|
-
it to the repository and confirms completion; bootstrap then proves repository
|
|
70
|
-
access before the first deployment.
|
|
71
|
-
|
|
72
|
-
Browser authentication is deliberately not automated away. If npm or another
|
|
73
|
-
provider prints an authentication URL, leave the command attached to its TTY,
|
|
74
|
-
open the URL, complete the passkey flow, and let the same command continue.
|
|
75
|
-
|
|
76
|
-
Platform database participation is explicit in the private JSON configuration.
|
|
77
|
-
The three genesis nodes use `database.agency: "member"`; later DB+API joiners
|
|
78
|
-
normally use `database.agency: "none"`, which starts a Coordinator and DBServer
|
|
79
|
-
without adding a fourth Agency member. Enrolment is likewise explicit:
|
|
80
|
-
`enrolment.source: "genesis-derived"` only for the offline genesis fleet, or
|
|
81
|
-
`"api-token"` with `tokenFile` for every API-authorized later node. No behavior
|
|
82
|
-
is inferred from a hostname or numeric suffix.
|
|
83
|
-
|
|
84
|
-
Repair is not a second installation path: the CLI detects the installed
|
|
85
|
-
platform/tenant or metal state and dispatches to that profile's validator. It
|
|
86
|
-
fails closed if both state formats are present or neither is present. A private
|
|
87
|
-
intent journal binds an interrupted first apply, and later repair refuses a
|
|
88
|
-
config whose immutable host identity differs from the installed profile. Status checks the persisted
|
|
89
|
-
identity record, both Agent sockets, profile-owned systemd units and, for a
|
|
90
|
-
platform host, the active API health endpoint, nginx configuration and live
|
|
91
|
-
Coordinator-mode evidence.
|
|
92
|
-
|
|
93
|
-
Platform bootstrap supports an elastic Community ArangoDB 3.11.14 fleet. The
|
|
94
|
-
first three database-capable computes establish the writable cluster; later
|
|
95
|
-
database joiners and API-only computes use the same command with their typed
|
|
96
|
-
profile and an API-issued one-time enrolment file. Nothing treats three as a
|
|
97
|
-
maximum.
|
|
98
|
-
|
|
99
|
-
After genesis, a tenant can designate an enrolled compute as a bootstrap
|
|
100
|
-
runner. The API creates a project-bound, expiring job containing only public
|
|
101
|
-
SSH coordinates and a pinned Ed25519 host-key fingerprint. The runner claims it
|
|
102
|
-
with its hybrid node identity, reads its SSH key only from a local systemd
|
|
103
|
-
credential, pins one vetted DNS answer, transfers this packaged CLI, and runs
|
|
104
|
-
the same `fz bootstrap tenant` flow on the target. Claim renewal, completion,
|
|
105
|
-
retry and cancellation are fenced in the compute aggregate; the API never
|
|
106
|
-
stores the SSH private key or a plaintext enrolment token.
|
|
107
|
-
|
|
108
|
-
The attended Cloudflare resource phase is part of the same published command,
|
|
109
|
-
but remains separate from the root host install so its management token never
|
|
110
|
-
enters the API or Agent service environment:
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
chmod 600 cloudflare-bootstrap.json cloudflare-management.token cloudflare-runtime-kv.token
|
|
114
|
-
fz bootstrap platform cloudflare --bootstrap-config ./cloudflare-bootstrap.json
|
|
115
|
-
fz bootstrap platform cloudflare --bootstrap-config ./cloudflare-bootstrap.json --apply
|
|
116
|
-
# After every host is bootstrapped and publishing healthy KV state:
|
|
117
|
-
fz bootstrap platform cloudflare verify --bootstrap-config ./cloudflare-handoff.json
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Tenant-owned public origins use the identical reviewed request and handoff with
|
|
121
|
-
`fz bootstrap tenant cloudflare ...`; `fz bootstrap tenant` consumes only that
|
|
122
|
-
tenant node's handoff. Both host kinds seal the connector and supplied KV token
|
|
123
|
-
and can repair after the plaintext handoff has been removed.
|
|
124
|
-
|
|
125
|
-
The reviewed JSON has `format: 1`, kind
|
|
126
|
-
`forgezero-cloudflare-bootstrap-request`, a `checkpointPath`, the typed
|
|
127
|
-
`coordinates`, and token **file paths only**. The recommended token split is:
|
|
128
|
-
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"format": 1,
|
|
132
|
-
"kind": "forgezero-cloudflare-bootstrap-request",
|
|
133
|
-
"checkpointPath": "./cloudflare-handoff.json",
|
|
134
|
-
"coordinates": {
|
|
135
|
-
"accountId": "0123456789abcdef0123456789abcdef",
|
|
136
|
-
"zoneId": "0123456789abcdef0123456789abcdef",
|
|
137
|
-
"hostname": "dev-api-n1.example.com",
|
|
138
|
-
"service": "http://127.0.0.1:3000",
|
|
139
|
-
"tunnelName": "dev-api-n1",
|
|
140
|
-
"kvNamespaceId": "0123456789abcdef0123456789abcdef",
|
|
141
|
-
"nodes": [
|
|
142
|
-
{
|
|
143
|
-
"nodeName": "dev-api-n1",
|
|
144
|
-
"hostname": "dev-api-n1.example.com",
|
|
145
|
-
"service": "http://127.0.0.1:3000",
|
|
146
|
-
"tunnelName": "dev-api-n1"
|
|
147
|
-
}
|
|
148
|
-
]
|
|
149
|
-
},
|
|
150
|
-
"tokenFiles": {
|
|
151
|
-
"managementApiTokenFile": "./cloudflare-management.token",
|
|
152
|
-
"runtimeApiTokenFile": "./cloudflare-runtime-kv.token"
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
The management token has exact-account **Cloudflare Tunnel Write** and
|
|
158
|
-
exact-zone **DNS Write**. The runtime token has exact-account **Workers KV
|
|
159
|
-
Storage Write**. Apply creates or adopts one remotely managed Tunnel per node,
|
|
160
|
-
checkpoints its connector token, configures the public-hostname ingress, and
|
|
161
|
-
reconciles the exact proxied CNAME. DNS reconciliation lists the hostname
|
|
162
|
-
without a type filter: it updates one existing CNAME, creates only when absent,
|
|
163
|
-
and refuses ambiguity or an incompatible A/AAAA/other owner before changing
|
|
164
|
-
Tunnel ingress. It does not create a Worker, KV namespace, Access application,
|
|
165
|
-
Access service token, or Cloudflare API token.
|
|
166
|
-
|
|
167
|
-
The Worker and its KV/DO bindings are deployed separately through Cloudflare's
|
|
168
|
-
Git integration. Durable Objects are reached through the Worker's binding, not
|
|
169
|
-
with a Cloudflare REST token. The complete 0600 checkpoint stays on the
|
|
170
|
-
operator machine; `<checkpoint>.hosts/<node>.json` contains only that node's
|
|
171
|
-
connector plus the supplied KV runtime token. Host bootstrap seals them as the
|
|
172
|
-
`cloudflared-token` and `cloudflare-kv-token` systemd credentials and removes
|
|
173
|
-
the plaintext node handoff. The API resolves `cloudflare.kvApiToken` from the
|
|
174
|
-
platform Vault first and falls back to the same systemd credential while the
|
|
175
|
-
Vault is locked or unavailable. Management credentials are never persisted or
|
|
176
|
-
loaded into the API/Agent service.
|
|
177
|
-
|
|
178
|
-
Platform and tenant bootstrap configs may declare a bounded
|
|
179
|
-
`deploymentCredentials` map from the exact pipeline secret name to its
|
|
180
|
-
encrypted `.cred` source, for example
|
|
181
|
-
`{"SMTP_PASSWORD":"/etc/forgezero/creds/SMTP_PASSWORD.cred"}`. The supervised
|
|
182
|
-
Agent loads only those named credentials. A deployment asks for the same name;
|
|
183
|
-
the Agent reads its project-scoped in-memory Vault replica first and uses the
|
|
184
|
-
same-name systemd credential only until the replica can supply it. There is no
|
|
185
|
-
directory scan, cross-project lookup, or differently named fallback.
|
|
186
|
-
|
|
187
|
-
This is a versioned runtime schema, not a naming convention. The enrolled
|
|
188
|
-
binding supplies the exact project and environment; `credential-schema.ts`
|
|
189
|
-
maps a requested name to that environment's Vault cache coordinate. Platform
|
|
190
|
-
and tenant computes use this Vault-first policy. Physical metal has no project
|
|
191
|
-
Vault and loads only its fixed `metal-agent-seed` systemd credential. Attended
|
|
192
|
-
operator Cloudflare/bootstrap tokens are owner-only input files and are never a
|
|
193
|
-
runtime fallback. The schema is exported as `@forgezero/agent/credential-schema`.
|
|
32
|
+
```
|
|
194
33
|
|
|
195
|
-
##
|
|
34
|
+
## fz
|
|
196
35
|
|
|
197
|
-
|
|
36
|
+
The operator command: keys, status, genesis, unlock, run, and agent install. This entry requires a Bun/Node host runtime.
|
|
198
37
|
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
const fz = new ForgeZero({ project: 'altpilot', environment: 'production' });
|
|
202
|
-
await fz.get('STRIPE_KEY');
|
|
38
|
+
```text
|
|
39
|
+
fz --help
|
|
203
40
|
```
|
|
204
41
|
|
|
205
|
-
|
|
206
|
-
agent, it does not — and the key that would have been in the environment does
|
|
207
|
-
not exist on the box.
|
|
42
|
+
## fz run
|
|
208
43
|
|
|
209
|
-
|
|
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.
|
|
210
45
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
cache only has what you fetched, so a secret you have never read is exactly the
|
|
215
|
-
one you cannot get when the platform is unreachable.
|
|
46
|
+
```text
|
|
47
|
+
fz run --help
|
|
48
|
+
```
|
|
216
49
|
|
|
217
|
-
|
|
218
|
-
scope rather than only what was read. It is bounded by the scope the compute is
|
|
219
|
-
assigned to, and by SEV-SNP keeping the host out of guest memory.
|
|
50
|
+
## fz agent install
|
|
220
51
|
|
|
221
|
-
|
|
222
|
-
every secret this guest has ever held — the exact artefact the design exists to
|
|
223
|
-
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.
|
|
224
53
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
54
|
+
```text
|
|
55
|
+
fz agent install --help
|
|
56
|
+
```
|
|
228
57
|
|
|
229
|
-
|
|
230
|
-
staleness bound, `get` fails rather than returning a value it can no longer
|
|
231
|
-
vouch for. An application that receives a revoked credential and succeeds with
|
|
232
|
-
it is worse off than one that receives an error — the error is visible.
|
|
58
|
+
## fz-agent
|
|
233
59
|
|
|
234
|
-
|
|
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
|
|
235
115
|
|
|
236
|
-
|
|
237
|
-
|---|---|
|
|
238
|
-
| `attested` | SEV-SNP guest. The credential is a hardware report, and the platform can refuse a node whose measurement is wrong. |
|
|
239
|
-
| `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.
|
|
240
117
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
+
```
|
|
246
161
|
|
|
247
|
-
|
|
248
|
-
evidence: report acquisition and AMD-rooted measurement verification still have
|
|
249
|
-
to cross the production fleet. Until then the node is honestly `enrolled`; the
|
|
250
|
-
Agent never manufactures an attestation-shaped fallback.
|
|
162
|
+
## @forgezero/agent/capacity-calibration
|
|
251
163
|
|
|
252
|
-
|
|
253
|
-
and signs its one-use hybrid ML-KEM-768 + X25519 response key. Vault replication,
|
|
254
|
-
deployment claims, attestation and metal provisioning responses are AES-256-GCM
|
|
255
|
-
sealed to that request before crossing the edge; there is no Ed25519-only or
|
|
256
|
-
successful plaintext-response downgrade.
|
|
164
|
+
One-time target-local safe-concurrency calibration with private evidence.
|
|
257
165
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
166
|
+
```text
|
|
167
|
+
import * as api from '@forgezero/agent/capacity-calibration';
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## @forgezero/agent/cloudflare-bootstrap
|
|
262
171
|
|
|
263
|
-
|
|
172
|
+
Attended Cloudflare Tunnel, KV and runtime-token bootstrap with private resumable handoff.
|
|
264
173
|
|
|
174
|
+
```text
|
|
175
|
+
import * as api from '@forgezero/agent/cloudflare-bootstrap';
|
|
265
176
|
```
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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';
|
|
269
184
|
```
|
|
270
185
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
node signature over a deployment or whole-project sync request and impersonate
|
|
275
|
-
the Agent without ever extracting its key. Deployment enters through the
|
|
276
|
-
root-only control socket or signed outbound claim path, then the deployment
|
|
277
|
-
manager validates the checked-in definition and submits it to the common keyed
|
|
278
|
-
queue.
|
|
186
|
+
## @forgezero/agent/compute
|
|
187
|
+
|
|
188
|
+
Compute claim, renewal, execution and completion client contracts.
|
|
279
189
|
|
|
280
|
-
|
|
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
|
+
```
|
|
281
201
|
|
|
282
|
-
|
|
283
|
-
Git-owned context and generate the small files each AI product discovers:
|
|
202
|
+
## @forgezero/agent/definition
|
|
284
203
|
|
|
285
|
-
|
|
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
|
|
286
312
|
fz project init
|
|
287
|
-
# edit .forgezero/project.json and create its named truth sources
|
|
288
313
|
fz project sync
|
|
289
314
|
fz project check
|
|
290
315
|
```
|
|
291
316
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
`fz
|
|
295
|
-
|
|
296
|
-
|
|
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
|
+
```
|
|
297
326
|
|
|
298
|
-
##
|
|
327
|
+
## One project memory for every AI tool
|
|
299
328
|
|
|
300
|
-
|
|
301
|
-
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.
|
|
302
330
|
|
|
303
|
-
```
|
|
304
|
-
fz
|
|
305
|
-
|
|
306
|
-
fz
|
|
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
|
|
307
336
|
```
|
|
308
337
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
+
```
|
|
313
347
|
|
|
314
|
-
|
|
315
|
-
the same API and access matrix:
|
|
348
|
+
## Use website operations from a project or an AI agent
|
|
316
349
|
|
|
317
|
-
|
|
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
|
|
318
354
|
fz ui routes pipeline
|
|
319
355
|
fz ui get /workspace/pipelines --query projectKey=my-project
|
|
320
|
-
fz api post /workspace/pipelines/status
|
|
321
|
-
--data '{"pipelineKey":"pl_…","enabled":false}'
|
|
356
|
+
fz api post /workspace/pipelines/status --data '{"pipelineKey":"pl_…","enabled":false}'
|
|
322
357
|
generate-request | fz api post /workspace/action --data -
|
|
323
358
|
```
|
|
324
359
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
##
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
`https://www.forgezero.net/schemas/deploy-v2.json`.
|
|
375
|
-
|
|
376
|
-
Git is the only local-to-live synchronization mechanism. `sync` validates and
|
|
377
|
-
prints that rule; it does not create a second mutable command copy in the API.
|
|
378
|
-
The verified webhook identifies one exact commit, and a successful Agent result
|
|
379
|
-
persists the definition digest so the Applications screen can prove which local
|
|
380
|
-
contract became live.
|
|
381
|
-
|
|
382
|
-
Catalog status is an admission boundary, not a suggestion. `testing` coordinates
|
|
383
|
-
are visible in the development catalog for ForgeZero qualification but cannot be
|
|
384
|
-
selected by any deploy file. A repository cannot promote software by calling
|
|
385
|
-
itself development; only a reviewed catalog change to `active` unlocks it.
|
|
386
|
-
|
|
387
|
-
The daemon owns source checkout and command execution. Bootstrap explicitly
|
|
388
|
-
awaits release one because the API does not exist yet, then the Agent consumes a
|
|
389
|
-
one-use platform enrolment capability. There is no branch watcher. Every normal
|
|
390
|
-
platform or tenant release is one durable API delivery atomically expanded to
|
|
391
|
-
one row for every independently attached compute target. Release-scoped steps
|
|
392
|
-
run on one target elected by stable target ordering, never on a user-declared
|
|
393
|
-
coordinator:
|
|
394
|
-
`pending` is written before dispatch, `running` and a fenced lease before project
|
|
395
|
-
code, and only an awaited successful pipeline writes `deployed`. An expired
|
|
396
|
-
claim can be recovered; its stale token cannot renew or finish.
|
|
397
|
-
|
|
398
|
-
Compute replacement uses the same outbound pattern through
|
|
399
|
-
`@forgezero/agent/migration-pull`: claim one action for this exact compute,
|
|
400
|
-
renew its fencing lease while the awaited function runs, and acknowledge the
|
|
401
|
-
result before accepting more work. The public function accepts the executor as
|
|
402
|
-
an async callback so an embedding Agent can keep privilege and OS policy outside
|
|
403
|
-
the transport. Node evidence is deliberately local-only; it cannot claim
|
|
404
|
-
Agency quorum, replication completion, seed residency, edge exclusion or
|
|
405
|
-
Cloudflare route existence on behalf of the controller.
|
|
406
|
-
|
|
407
|
-
The managed daemon uses `@forgezero/agent/lifecycle-helper` as that executor.
|
|
408
|
-
It is a separate root service behind a group-scoped Unix socket and accepts only
|
|
409
|
-
fixed lifecycle operations. Unit names and loopback health endpoints come from
|
|
410
|
-
a root-owned profile; a signed claim cannot supply a command or path. For a
|
|
411
|
-
cross-network database member, the common installer enrolls the headless WARP
|
|
412
|
-
client from systemd credentials materialized only in `/run`. Readiness requires
|
|
413
|
-
WARP to be connected and the controller-selected private peer IP and database
|
|
414
|
-
port to be reachable. Database traffic is never exposed through a public IP,
|
|
415
|
-
public hostname, or inbound firewall rule.
|
|
416
|
-
|
|
417
|
-
The same package also runs the identity-only Metal Agent. It initiates outbound
|
|
418
|
-
hybrid-signed HTTPS and requires request-bound PQ-sealed responses while pulling
|
|
419
|
-
only claims assigned to its enrolled hostname, then passes
|
|
420
|
-
the fixed claim over a local Unix socket to a narrowly privileged root helper.
|
|
421
|
-
The helper can materialize the audited QEMU profile; it cannot clone a project,
|
|
422
|
-
read Vault data, accept arbitrary commands, or retain tenant credentials. Root
|
|
423
|
-
SSH is an operator-only platform recovery path, not the normal tenant
|
|
424
|
-
dispatch mechanism.
|
|
425
|
-
|
|
426
|
-
New guests also call this same public `fz agent install --apply --enrol` path
|
|
427
|
-
from cloud-init. Metal provisioning does not carry a second handwritten Agent
|
|
428
|
-
unit or deployment runner, so platform and tenant guests cannot drift at that
|
|
429
|
-
foundation boundary.
|
|
430
|
-
|
|
431
|
-
Shutdown has one process-wide deadline. New deployment claims and background
|
|
432
|
-
sync stop first, claimed work drains under its lease fence, and only then do the
|
|
433
|
-
queues and application socket close. Vault/attestation calls or a stale local
|
|
434
|
-
socket cannot consume systemd's longer stop timeout: exceeding the Agent deadline
|
|
435
|
-
is reported and exits non-zero instead of being silently killed midway by PID 1.
|
|
436
|
-
|
|
437
|
-
Repository read authorization is explicit per pipeline: public HTTPS, the
|
|
438
|
-
compute's systemd-sealed SSH deploy key, or a fine-grained HTTPS token selected
|
|
439
|
-
by a project-vault secret name. A signed claim contains the mode and secret name
|
|
440
|
-
only. The agent resolves the value from its scoped memory cache, limits the Git
|
|
441
|
-
header to the repository origin, never writes the token into a command, and
|
|
442
|
-
never exposes it to tenant pipeline steps. These provider-neutral modes are the
|
|
443
|
-
complete contract; a GitHub App is not a missing dependency and would be added
|
|
444
|
-
later only as an explicit provider-specific mode with expiry and revocation tests.
|
|
445
|
-
|
|
446
|
-
An operator may also force a deployment and await the complete result over the
|
|
447
|
-
private control socket:
|
|
448
|
-
|
|
449
|
-
```bash
|
|
450
|
-
fz-agent deploy --revision=<full-40-character-commit> --release-executor
|
|
451
|
-
fz-agent status
|
|
452
|
-
fz-agent pause
|
|
453
|
-
fz-agent pause-key --key=project:production
|
|
454
|
-
fz-agent stop-key --key=project:production # running finishes; pending work is removed
|
|
455
|
-
fz-agent start-key --key=project:production
|
|
456
|
-
fz-agent cancel --id=q_42
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
The node seed and read-only Git private key are loaded with
|
|
460
|
-
`LoadCredentialEncrypted=`. Their encrypted, host-bound blobs persist under
|
|
461
|
-
`/etc/forgezero/creds`; decrypted values exist only in the service's private
|
|
462
|
-
`$CREDENTIALS_DIRECTORY`. Platform bootstrap has no second clone/build path.
|
|
463
|
-
Bootstrap records the forge's SSH host key during the attended access check;
|
|
464
|
-
the daemon uses `StrictHostKeyChecking=yes` and never accepts a new host key by
|
|
465
|
-
itself.
|
|
466
|
-
|
|
467
|
-
Full documentation: **https://www.forgezero.net/docs/agent**
|
|
468
|
-
|
|
469
|
-
## Licence
|
|
470
|
-
|
|
471
|
-
MIT. Part of [ForgeZero](https://www.forgezero.net) — secrets, attested compute and
|
|
472
|
-
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
|