@forgezero/agent 0.1.72 → 0.1.74

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
@@ -35,48 +35,48 @@ Every row links to the detailed explanation and named-import/example area below.
35
35
 
36
36
  | public entry | short description | runtime | details |
37
37
  |---|---|---|---|
38
- | fz | The operator command: keys, status, genesis, unlock, run, and agent install. | Bun/Node host | [Details + example](#fz) |
39
- | fz run | The fallback for software that cannot read the local socket: start a process with scoped vault values in its environment. | Bun/Node host | [Details + example](#fz-run) |
40
- | fz agent install | Provision the same hardened agent service locally that the platform provisions remotely. | Bun/Node host | [Details + example](#fz-agent-install) |
41
- | fz-agent | 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 builds each deployment manager from its signed server claim and reads all commands from the checked-out .fz definition. The signed enrolment binding constructs the exact project Vault replica scope, and an SNP guest completes its first live attestation before Vault synchronization begins. | Bun/Node host | [Details + example](#fz-agent) |
42
- | fz-agent (socket) | identity, sign, attest, get, sync and held — the whole interface an application sees. | Bun/Node host | [Details + example](#fz-agent-socket) |
43
- | @forgezero/agent/migration-pull | PQ-authenticated outbound lifecycle claims with fenced renewal, awaited async execution, idempotent acknowledgement and bounded shutdown drain. The executor reports local facts only. | Bun/Node host | [Details + example](#forgezero-agent-migration-pull) |
44
- | @forgezero/agent/lifecycle-helper | 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. | Bun/Node host | [Details + example](#forgezero-agent-lifecycle-helper) |
45
- | @forgezero/agent/provision | The install plan as data, with no transport — so `fz` running locally and the platform running over SSH provision an identical machine. | Bun/Node host | [Details + example](#forgezero-agent-provision) |
46
- | @forgezero/agent/platform-genesis | Offline authority adapter for the brief pre-API period. It validates exactly three reviewed generic guest requests and converts them to the same Metal claims the live API issues; environment and product tier are not provisioning inputs. | Bun/Node host | [Details + example](#forgezero-agent-platform-genesis) |
47
- | @forgezero/agent/subscribe | 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. | Bun/Node host | [Details + example](#forgezero-agent-subscribe) |
48
- | @forgezero/agent/ssh-listen | 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. | Bun/Node host | [Details + example](#forgezero-agent-ssh-listen) |
49
- | @forgezero/agent/ssh-server | 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. | Bun/Node host | [Details + example](#forgezero-agent-ssh-server) |
50
- | @forgezero/agent/pipeline | 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. | Bun/Node host | [Details + example](#forgezero-agent-pipeline) |
51
- | @forgezero/agent/agent-heartbeat | Signed node health publication with bounded renewal and shutdown withdrawal. | Bun/Node host | [Details + example](#forgezero-agent-agent-heartbeat) |
52
- | @forgezero/agent/agent-update | Verified Agent update planning against the platform release coordinate. | Bun/Node host | [Details + example](#forgezero-agent-agent-update) |
53
- | @forgezero/agent/agent-update-helper | Root-owned fixed update operations with registry origin and integrity validation. | Bun/Node host | [Details + example](#forgezero-agent-agent-update-helper) |
54
- | @forgezero/agent/bootstrap | Typed one-time platform bootstrap and internal API-driven enrolled-compute activation contracts. | Bun/Node host | [Details + example](#forgezero-agent-bootstrap) |
55
- | @forgezero/agent/bootstrap-bundle | Owner-only exact-revision Git bundle and SHA-256 manifest for credential-free release generation one. | Bun/Node host | [Details + example](#forgezero-agent-bootstrap-bundle) |
56
- | @forgezero/agent/operator-bootstrap | Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. | Bun/Node host | [Details + example](#forgezero-agent-operator-bootstrap) |
57
- | @forgezero/agent/host-maintenance | Closed host maintenance plans for staging transaction rehearsal and fleet-fenced schema consolidation through fixed transient systemd units. | Bun/Node host | [Details + example](#forgezero-agent-host-maintenance) |
58
- | @forgezero/agent/capacity-calibration | One-time target-local safe-concurrency calibration with private evidence. | Bun/Node host | [Details + example](#forgezero-agent-capacity-calibration) |
59
- | @forgezero/agent/cloudflare-bootstrap | Attended two-token Cloudflare Tunnel/DNS reconciliation and per-node connector/KV runtime handoff for existing KV resources. | Bun/Node host | [Details + example](#forgezero-agent-cloudflare-bootstrap) |
60
- | @forgezero/agent/cloudflare-edge | Strict Cloudflare REST operations used by the attended bootstrap controller. | Bun/Node host | [Details + example](#forgezero-agent-cloudflare-edge) |
61
- | @forgezero/agent/mesh-connector | Fixed Cloudflare Mesh/WARP connector enrollment using only the unit-loaded connector credential. | Bun/Node host | [Details + example](#forgezero-agent-mesh-connector) |
62
- | @forgezero/agent/compute | Compute claim, renewal, execution and completion client contracts. | Bun/Node host | [Details + example](#forgezero-agent-compute) |
63
- | @forgezero/agent/credential-schema | Canonical systemd credential names and validation shared by bootstrap and services. | Bun/Node host | [Details + example](#forgezero-agent-credential-schema) |
64
- | @forgezero/agent/definition | Legacy version 3 JSON deployment compatibility contract. | Bun/Node host | [Details + example](#forgezero-agent-definition) |
65
- | @forgezero/agent/deploy | Typed deployment authoring builders, built-in provider requirements and versioned action constructors. | Bun/Node host | [Details + example](#forgezero-agent-deploy) |
66
- | @forgezero/agent/deploy-plan | Runtime validation, canonicalization and semantic digest for inert deployment plans. | Bun/Node host | [Details + example](#forgezero-agent-deploy-plan) |
67
- | @forgezero/agent/deploy-plan-runner | Conditional DAG execution with bounded retry, compensation and failure policy over injected action handlers. | Bun/Node host | [Details + example](#forgezero-agent-deploy-plan-runner) |
68
- | @forgezero/agent/deploy-compiler | Explicit developer/CI compiler from forgezero.deploy.ts to canonical .fz/deploy.plan.json; never used to execute project TypeScript in production. | Bun/Node host | [Details + example](#forgezero-agent-deploy-compiler) |
69
- | @forgezero/agent/deploy-file | Read and validate a checked-out .fz deployment file without executing project input. | Bun/Node host | [Details + example](#forgezero-agent-deploy-file) |
70
- | @forgezero/agent/metal-bootstrap | Physical-metal bootstrap planning, application and status evidence. | Bun/Node host | [Details + example](#forgezero-agent-metal-bootstrap) |
71
- | @forgezero/agent/metal-provision | The single constrained compute materializer for platform genesis, platform scaling and tenant compute. It executes caller-supplied OS, CPU, memory, disk, bandwidth, network and confidentiality coordinates after capacity and image validation; it owns no environment or product-tier shape. | Bun/Node host | [Details + example](#forgezero-agent-metal-provision) |
72
- | @forgezero/agent/platform-bootstrap-runtime | Pure platform bootstrap validation, render and resumable phase-state contracts. | Bun/Node host | [Details + example](#forgezero-agent-platform-bootstrap-runtime) |
73
- | @forgezero/agent/project-context | Project identity and repository context validation for operator and managed runs. | Bun/Node host | [Details + example](#forgezero-agent-project-context) |
74
- | @forgezero/agent/provisioning-pull | Signed provisioning work claim, renewal, execution and acknowledgement. | Bun/Node host | [Details + example](#forgezero-agent-provisioning-pull) |
75
- | @forgezero/agent/schema/deploy-v3.json | Published JSON Schema for deployment definition version 3 with exact argv execution. | Bun/Node host | [Details + example](#forgezero-agent-schema-deploy-v3-json) |
76
- | @forgezero/agent/software | Exact managed-software inventory, requirement and evidence contracts. | Bun/Node host | [Details + example](#forgezero-agent-software) |
77
- | @forgezero/agent/software-helper | Root-owned fixed installation/update boundary for declared software. | Bun/Node host | [Details + example](#forgezero-agent-software-helper) |
78
- | @forgezero/agent/ubuntu | The closed guest-image view: pinned Ubuntu 26.04 LTS x64 supports ordinary or SEV-SNP compute, while pinned Ubuntu 24.04 LTS x64 is limited to ordinary non-confidential compute. Every other OS remains rejected until its own pinned strategy and acceptance evidence are added. | Bun/Node host | [Details + example](#forgezero-agent-ubuntu) |
79
- | fz-agent (replica) | 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. | Bun/Node host | [Details + example](#fz-agent-replica) |
38
+ | fz | The operator command: keys, status, genesis, unlock, run, and agent install. | Bun/Node host | [Reference + usage](#fz) |
39
+ | fz run | The fallback for software that cannot read the local socket: start a process with scoped vault values in its environment. | Bun/Node host | [Reference + usage](#fz-run) |
40
+ | fz agent install | Provision the same hardened agent service locally that the platform provisions remotely. | Bun/Node host | [Reference + usage](#fz-agent-install) |
41
+ | fz-agent | 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 builds each deployment manager from its signed server claim and reads all commands from the checked-out .fz definition. The signed enrolment binding constructs the exact project Vault replica scope, and an SNP guest completes its first live attestation before Vault synchronization begins. | Bun/Node host | [Reference + usage](#fz-agent) |
42
+ | fz-agent (socket) | identity, sign, attest, get, sync and held — the whole interface an application sees. | Bun/Node host | [Reference + usage](#fz-agent-socket) |
43
+ | @forgezero/agent/migration-pull | PQ-authenticated outbound lifecycle claims with fenced renewal, awaited async execution, idempotent acknowledgement and bounded shutdown drain. The executor reports local facts only. | Bun/Node host | [Reference + usage](#forgezero-agent-migration-pull) |
44
+ | @forgezero/agent/lifecycle-helper | 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. | Bun/Node host | [Reference + usage](#forgezero-agent-lifecycle-helper) |
45
+ | @forgezero/agent/provision | The install plan as data, with no transport — so `fz` running locally and the platform running over SSH provision an identical machine. | Bun/Node host | [Reference + usage](#forgezero-agent-provision) |
46
+ | @forgezero/agent/platform-genesis | Offline authority adapter for the brief pre-API period. It validates exactly three reviewed generic guest requests and converts them to the same Metal claims the live API issues; environment and product tier are not provisioning inputs. | Bun/Node host | [Reference + usage](#forgezero-agent-platform-genesis) |
47
+ | @forgezero/agent/subscribe | 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. | Bun/Node host | [Reference + usage](#forgezero-agent-subscribe) |
48
+ | @forgezero/agent/ssh-listen | 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. | Bun/Node host | [Reference + usage](#forgezero-agent-ssh-listen) |
49
+ | @forgezero/agent/ssh-server | 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. | Bun/Node host | [Reference + usage](#forgezero-agent-ssh-server) |
50
+ | @forgezero/agent/pipeline | 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. | Bun/Node host | [Reference + usage](#forgezero-agent-pipeline) |
51
+ | @forgezero/agent/agent-heartbeat | Signed node health publication with bounded renewal and shutdown withdrawal. | Bun/Node host | [Reference + usage](#forgezero-agent-agent-heartbeat) |
52
+ | @forgezero/agent/agent-update | Verified Agent update planning against the platform release coordinate. | Bun/Node host | [Reference + usage](#forgezero-agent-agent-update) |
53
+ | @forgezero/agent/agent-update-helper | Root-owned fixed update operations with registry origin and integrity validation. | Bun/Node host | [Reference + usage](#forgezero-agent-agent-update-helper) |
54
+ | @forgezero/agent/bootstrap | Typed one-time platform bootstrap and internal API-driven enrolled-compute activation contracts. | Bun/Node host | [Reference + usage](#forgezero-agent-bootstrap) |
55
+ | @forgezero/agent/bootstrap-bundle | Owner-only exact-revision Git bundle and SHA-256 manifest for credential-free release generation one. | Bun/Node host | [Reference + usage](#forgezero-agent-bootstrap-bundle) |
56
+ | @forgezero/agent/operator-bootstrap | Pinned-host operator transport that stages owner-only handoffs and invokes the same typed platform bootstrap through the caller-approved SSH agent. | Bun/Node host | [Reference + usage](#forgezero-agent-operator-bootstrap) |
57
+ | @forgezero/agent/host-maintenance | Closed host maintenance plans for staging transaction rehearsal and fleet-fenced schema consolidation through fixed transient systemd units. | Bun/Node host | [Reference + usage](#forgezero-agent-host-maintenance) |
58
+ | @forgezero/agent/capacity-calibration | One-time target-local safe-concurrency calibration with private evidence. | Bun/Node host | [Reference + usage](#forgezero-agent-capacity-calibration) |
59
+ | @forgezero/agent/cloudflare-bootstrap | Attended two-token Cloudflare Tunnel/DNS reconciliation and per-node connector/KV runtime handoff for existing KV resources. | Bun/Node host | [Reference + usage](#forgezero-agent-cloudflare-bootstrap) |
60
+ | @forgezero/agent/cloudflare-edge | Strict Cloudflare REST operations used by the attended bootstrap controller. | Bun/Node host | [Reference + usage](#forgezero-agent-cloudflare-edge) |
61
+ | @forgezero/agent/mesh-connector | Fixed Cloudflare Mesh/WARP connector enrollment using only the unit-loaded connector credential. | Bun/Node host | [Reference + usage](#forgezero-agent-mesh-connector) |
62
+ | @forgezero/agent/compute | Compute claim, renewal, execution and completion client contracts. | Bun/Node host | [Reference + usage](#forgezero-agent-compute) |
63
+ | @forgezero/agent/credential-schema | Canonical systemd credential names and validation shared by bootstrap and services. | Bun/Node host | [Reference + usage](#forgezero-agent-credential-schema) |
64
+ | @forgezero/agent/definition | Legacy version 3 JSON deployment compatibility contract. | Bun/Node host | [Reference + usage](#forgezero-agent-definition) |
65
+ | @forgezero/agent/deploy | Typed deployment authoring builders, built-in provider requirements and versioned action constructors. | Bun/Node host | [Reference + usage](#forgezero-agent-deploy) |
66
+ | @forgezero/agent/deploy-plan | Runtime validation, canonicalization and semantic digest for inert deployment plans. | Bun/Node host | [Reference + usage](#forgezero-agent-deploy-plan) |
67
+ | @forgezero/agent/deploy-plan-runner | Conditional DAG execution with bounded retry, compensation and failure policy over injected action handlers. | Bun/Node host | [Reference + usage](#forgezero-agent-deploy-plan-runner) |
68
+ | @forgezero/agent/deploy-compiler | Explicit developer/CI compiler from forgezero.deploy.ts to canonical .fz/deploy.plan.json; never used to execute project TypeScript in production. | Bun/Node host | [Reference + usage](#forgezero-agent-deploy-compiler) |
69
+ | @forgezero/agent/deploy-file | Read and validate a checked-out .fz deployment file without executing project input. | Bun/Node host | [Reference + usage](#forgezero-agent-deploy-file) |
70
+ | @forgezero/agent/metal-bootstrap | Physical-metal bootstrap planning, application and status evidence. | Bun/Node host | [Reference + usage](#forgezero-agent-metal-bootstrap) |
71
+ | @forgezero/agent/metal-provision | The single constrained compute materializer for platform genesis, platform scaling and tenant compute. It executes caller-supplied OS, CPU, memory, disk, bandwidth, network and confidentiality coordinates after capacity and image validation; it owns no environment or product-tier shape. | Bun/Node host | [Reference + usage](#forgezero-agent-metal-provision) |
72
+ | @forgezero/agent/platform-bootstrap-runtime | Pure platform bootstrap validation, render and resumable phase-state contracts. | Bun/Node host | [Reference + usage](#forgezero-agent-platform-bootstrap-runtime) |
73
+ | @forgezero/agent/project-context | Project identity and repository context validation for operator and managed runs. | Bun/Node host | [Reference + usage](#forgezero-agent-project-context) |
74
+ | @forgezero/agent/provisioning-pull | Signed provisioning work claim, renewal, execution and acknowledgement. | Bun/Node host | [Reference + usage](#forgezero-agent-provisioning-pull) |
75
+ | @forgezero/agent/schema/deploy-v3.json | Published JSON Schema for deployment definition version 3 with exact argv execution. | Bun/Node host | [Reference + usage](#forgezero-agent-schema-deploy-v3-json) |
76
+ | @forgezero/agent/software | Exact managed-software inventory, requirement and evidence contracts. | Bun/Node host | [Reference + usage](#forgezero-agent-software) |
77
+ | @forgezero/agent/software-helper | Root-owned fixed installation/update boundary for declared software. | Bun/Node host | [Reference + usage](#forgezero-agent-software-helper) |
78
+ | @forgezero/agent/ubuntu | The closed guest-image view: pinned Ubuntu 26.04 LTS x64 supports ordinary or SEV-SNP compute, while pinned Ubuntu 24.04 LTS x64 is limited to ordinary non-confidential compute. Every other OS remains rejected until its own pinned strategy and acceptance evidence are added. | Bun/Node host | [Reference + usage](#forgezero-agent-ubuntu) |
79
+ | fz-agent (replica) | 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. | Bun/Node host | [Reference + usage](#fz-agent-replica) |
80
80
 
81
81
  ## Commands
82
82
 
@@ -132,6 +132,14 @@ The fallback for software that cannot read the local socket: start a process wit
132
132
  fz run --help
133
133
  ```
134
134
 
135
+ ## fz run — Inspect before use
136
+
137
+ This is a command entry. Read its typed option contract before an operation; apply-capable commands remain plans unless their explicit apply flag is supplied.
138
+
139
+ ```text
140
+ fz run --help
141
+ ```
142
+
135
143
  <a id="fz-agent-install"></a>
136
144
  ## fz agent install
137
145
 
@@ -141,6 +149,14 @@ Provision the same hardened agent service locally that the platform provisions r
141
149
  fz agent install --help
142
150
  ```
143
151
 
152
+ ## fz agent install — Inspect before use
153
+
154
+ This is a command entry. Read its typed option contract before an operation; apply-capable commands remain plans unless their explicit apply flag is supplied.
155
+
156
+ ```text
157
+ fz agent install --help
158
+ ```
159
+
144
160
  <a id="fz-agent"></a>
145
161
  ## fz-agent
146
162
 
@@ -150,13 +166,22 @@ The service entry point. Identity and private Git material arrive only as system
150
166
  fz-agent --help
151
167
  ```
152
168
 
169
+ ## fz-agent — Inspect before use
170
+
171
+ This is a command entry. Read its typed option contract before an operation; apply-capable commands remain plans unless their explicit apply flag is supplied.
172
+
173
+ ```text
174
+ fz-agent --help
175
+ ```
176
+
153
177
  <a id="fz-agent-socket"></a>
154
178
  ## fz-agent (socket)
155
179
 
156
- identity, sign, attest, get, sync and held — the whole interface an application sees. This entry requires a Bun/Node host runtime. Run the command with `--help` before applying it.
180
+ identity, sign, attest, get, sync and held — the whole interface an application sees. This entry requires a Bun/Node host runtime. This is a managed runtime interface label, not an import or standalone executable.
157
181
 
158
182
  ```text
159
- fz-agent (socket) --help
183
+ fz-agent (socket)
184
+ # Accessed through the installed Agent boundary.
160
185
  ```
161
186
 
162
187
  ## fz-agent (socket) — Outbound control boundary
@@ -190,6 +215,18 @@ import type {
190
215
  } from '@forgezero/agent/migration-pull';
191
216
  ```
192
217
 
218
+ ## @forgezero/agent/migration-pull — Use this entry point
219
+
220
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
221
+
222
+ ```text
223
+ import {
224
+ pullMigrationOnce,
225
+ } from '@forgezero/agent/migration-pull';
226
+
227
+ export const selectedCapability = pullMigrationOnce;
228
+ ```
229
+
193
230
  <a id="forgezero-agent-lifecycle-helper"></a>
194
231
  ## @forgezero/agent/lifecycle-helper
195
232
 
@@ -213,6 +250,18 @@ import type {
213
250
  } from '@forgezero/agent/lifecycle-helper';
214
251
  ```
215
252
 
253
+ ## @forgezero/agent/lifecycle-helper — Use this entry point
254
+
255
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
256
+
257
+ ```text
258
+ import {
259
+ DEFAULT_LIFECYCLE_HELPER_SOCKET,
260
+ } from '@forgezero/agent/lifecycle-helper';
261
+
262
+ export const selectedCapability = DEFAULT_LIFECYCLE_HELPER_SOCKET;
263
+ ```
264
+
216
265
  <a id="forgezero-agent-provision"></a>
217
266
  ## @forgezero/agent/provision
218
267
 
@@ -271,6 +320,18 @@ import type {
271
320
  } from '@forgezero/agent/provision';
272
321
  ```
273
322
 
323
+ ## @forgezero/agent/provision — Use this entry point
324
+
325
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
326
+
327
+ ```text
328
+ import {
329
+ AGENT_EGRESS_UNIT_PATH,
330
+ } from '@forgezero/agent/provision';
331
+
332
+ export const selectedCapability = AGENT_EGRESS_UNIT_PATH;
333
+ ```
334
+
274
335
  <a id="forgezero-agent-platform-genesis"></a>
275
336
  ## @forgezero/agent/platform-genesis
276
337
 
@@ -289,6 +350,18 @@ import type {
289
350
  } from '@forgezero/agent/platform-genesis';
290
351
  ```
291
352
 
353
+ ## @forgezero/agent/platform-genesis — Use this entry point
354
+
355
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
356
+
357
+ ```text
358
+ import {
359
+ platformCommunityRehearsalClaims,
360
+ } from '@forgezero/agent/platform-genesis';
361
+
362
+ export const selectedCapability = platformCommunityRehearsalClaims;
363
+ ```
364
+
292
365
  <a id="forgezero-agent-subscribe"></a>
293
366
  ## @forgezero/agent/subscribe
294
367
 
@@ -308,6 +381,18 @@ import type {
308
381
  } from '@forgezero/agent/subscribe';
309
382
  ```
310
383
 
384
+ ## @forgezero/agent/subscribe — Use this entry point
385
+
386
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
387
+
388
+ ```text
389
+ import {
390
+ backoffMs,
391
+ } from '@forgezero/agent/subscribe';
392
+
393
+ export const selectedCapability = backoffMs;
394
+ ```
395
+
311
396
  <a id="forgezero-agent-ssh-listen"></a>
312
397
  ## @forgezero/agent/ssh-listen
313
398
 
@@ -324,6 +409,18 @@ import type {
324
409
  } from '@forgezero/agent/ssh-listen';
325
410
  ```
326
411
 
412
+ ## @forgezero/agent/ssh-listen — Use this entry point
413
+
414
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
415
+
416
+ ```text
417
+ import {
418
+ MAX_MESSAGE_BYTES,
419
+ } from '@forgezero/agent/ssh-listen';
420
+
421
+ export const selectedCapability = MAX_MESSAGE_BYTES;
422
+ ```
423
+
327
424
  <a id="forgezero-agent-ssh-server"></a>
328
425
  ## @forgezero/agent/ssh-server
329
426
 
@@ -349,6 +446,18 @@ import type {
349
446
  } from '@forgezero/agent/ssh-server';
350
447
  ```
351
448
 
449
+ ## @forgezero/agent/ssh-server — Use this entry point
450
+
451
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
452
+
453
+ ```text
454
+ import {
455
+ MAX_MESSAGE_BYTES,
456
+ } from '@forgezero/agent/ssh-server';
457
+
458
+ export const selectedCapability = MAX_MESSAGE_BYTES;
459
+ ```
460
+
352
461
  <a id="forgezero-agent-pipeline"></a>
353
462
  ## @forgezero/agent/pipeline
354
463
 
@@ -371,6 +480,18 @@ import type {
371
480
  } from '@forgezero/agent/pipeline';
372
481
  ```
373
482
 
483
+ ## @forgezero/agent/pipeline — Use this entry point
484
+
485
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
486
+
487
+ ```text
488
+ import {
489
+ PipelineError,
490
+ } from '@forgezero/agent/pipeline';
491
+
492
+ export const selectedCapability = PipelineError;
493
+ ```
494
+
374
495
  <a id="forgezero-agent-agent-heartbeat"></a>
375
496
  ## @forgezero/agent/agent-heartbeat
376
497
 
@@ -390,6 +511,18 @@ import type {
390
511
  } from '@forgezero/agent/agent-heartbeat';
391
512
  ```
392
513
 
514
+ ## @forgezero/agent/agent-heartbeat — Use this entry point
515
+
516
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
517
+
518
+ ```text
519
+ import {
520
+ heartbeatAgentOnce,
521
+ } from '@forgezero/agent/agent-heartbeat';
522
+
523
+ export const selectedCapability = heartbeatAgentOnce;
524
+ ```
525
+
393
526
  <a id="forgezero-agent-agent-update"></a>
394
527
  ## @forgezero/agent/agent-update
395
528
 
@@ -415,6 +548,18 @@ import type {
415
548
  } from '@forgezero/agent/agent-update';
416
549
  ```
417
550
 
551
+ ## @forgezero/agent/agent-update — Use this entry point
552
+
553
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
554
+
555
+ ```text
556
+ import {
557
+ DEFAULT_AGENT_RELEASE_ROOT,
558
+ } from '@forgezero/agent/agent-update';
559
+
560
+ export const selectedCapability = DEFAULT_AGENT_RELEASE_ROOT;
561
+ ```
562
+
418
563
  <a id="forgezero-agent-agent-update-helper"></a>
419
564
  ## @forgezero/agent/agent-update-helper
420
565
 
@@ -442,6 +587,18 @@ import type {
442
587
  } from '@forgezero/agent/agent-update-helper';
443
588
  ```
444
589
 
590
+ ## @forgezero/agent/agent-update-helper — Use this entry point
591
+
592
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
593
+
594
+ ```text
595
+ import {
596
+ AGENT_UPDATE_GROUP,
597
+ } from '@forgezero/agent/agent-update-helper';
598
+
599
+ export const selectedCapability = AGENT_UPDATE_GROUP;
600
+ ```
601
+
445
602
  <a id="forgezero-agent-bootstrap"></a>
446
603
  ## @forgezero/agent/bootstrap
447
604
 
@@ -486,6 +643,18 @@ import type {
486
643
  } from '@forgezero/agent/bootstrap';
487
644
  ```
488
645
 
646
+ ## @forgezero/agent/bootstrap — Use this entry point
647
+
648
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
649
+
650
+ ```text
651
+ import {
652
+ BOOTSTRAP_STATE_PATH,
653
+ } from '@forgezero/agent/bootstrap';
654
+
655
+ export const selectedCapability = BOOTSTRAP_STATE_PATH;
656
+ ```
657
+
489
658
  <a id="forgezero-agent-bootstrap-bundle"></a>
490
659
  ## @forgezero/agent/bootstrap-bundle
491
660
 
@@ -641,6 +810,18 @@ import type {
641
810
  } from '@forgezero/agent/capacity-calibration';
642
811
  ```
643
812
 
813
+ ## @forgezero/agent/capacity-calibration — Use this entry point
814
+
815
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
816
+
817
+ ```text
818
+ import {
819
+ calibrateHttpConcurrency,
820
+ } from '@forgezero/agent/capacity-calibration';
821
+
822
+ export const selectedCapability = calibrateHttpConcurrency;
823
+ ```
824
+
644
825
  <a id="forgezero-agent-cloudflare-bootstrap"></a>
645
826
  ## @forgezero/agent/cloudflare-bootstrap
646
827
 
@@ -685,6 +866,18 @@ import type {
685
866
  } from '@forgezero/agent/cloudflare-bootstrap';
686
867
  ```
687
868
 
869
+ ## @forgezero/agent/cloudflare-bootstrap — Use this entry point
870
+
871
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
872
+
873
+ ```text
874
+ import {
875
+ applyCloudflareBootstrap,
876
+ } from '@forgezero/agent/cloudflare-bootstrap';
877
+
878
+ export const selectedCapability = applyCloudflareBootstrap;
879
+ ```
880
+
688
881
  <a id="forgezero-agent-cloudflare-edge"></a>
689
882
  ## @forgezero/agent/cloudflare-edge
690
883
 
@@ -720,6 +913,18 @@ import type {
720
913
  } from '@forgezero/agent/cloudflare-edge';
721
914
  ```
722
915
 
916
+ ## @forgezero/agent/cloudflare-edge — Use this entry point
917
+
918
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
919
+
920
+ ```text
921
+ import {
922
+ configureCloudflareEdge,
923
+ } from '@forgezero/agent/cloudflare-edge';
924
+
925
+ export const selectedCapability = configureCloudflareEdge;
926
+ ```
927
+
723
928
  <a id="forgezero-agent-mesh-connector"></a>
724
929
  ## @forgezero/agent/mesh-connector
725
930
 
@@ -737,6 +942,18 @@ import type {
737
942
  } from '@forgezero/agent/mesh-connector';
738
943
  ```
739
944
 
945
+ ## @forgezero/agent/mesh-connector — Use this entry point
946
+
947
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
948
+
949
+ ```text
950
+ import {
951
+ configureMeshConnector,
952
+ } from '@forgezero/agent/mesh-connector';
953
+
954
+ export const selectedCapability = configureMeshConnector;
955
+ ```
956
+
740
957
  <a id="forgezero-agent-compute"></a>
741
958
  ## @forgezero/agent/compute
742
959
 
@@ -761,6 +978,18 @@ import type {
761
978
  } from '@forgezero/agent/compute';
762
979
  ```
763
980
 
981
+ ## @forgezero/agent/compute — Use this entry point
982
+
983
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
984
+
985
+ ```text
986
+ import {
987
+ ComputeError,
988
+ } from '@forgezero/agent/compute';
989
+
990
+ export const selectedCapability = ComputeError;
991
+ ```
992
+
764
993
  <a id="forgezero-agent-credential-schema"></a>
765
994
  ## @forgezero/agent/credential-schema
766
995
 
@@ -784,6 +1013,18 @@ import type {
784
1013
  } from '@forgezero/agent/credential-schema';
785
1014
  ```
786
1015
 
1016
+ ## @forgezero/agent/credential-schema — Use this entry point
1017
+
1018
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1019
+
1020
+ ```text
1021
+ import {
1022
+ AGENT_CREDENTIAL_LOCATIONS,
1023
+ } from '@forgezero/agent/credential-schema';
1024
+
1025
+ export const selectedCapability = AGENT_CREDENTIAL_LOCATIONS;
1026
+ ```
1027
+
787
1028
  <a id="forgezero-agent-definition"></a>
788
1029
  ## @forgezero/agent/definition
789
1030
 
@@ -808,6 +1049,18 @@ import type {
808
1049
  } from '@forgezero/agent/definition';
809
1050
  ```
810
1051
 
1052
+ ## @forgezero/agent/definition — Use this entry point
1053
+
1054
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1055
+
1056
+ ```text
1057
+ import {
1058
+ DEPLOY_SCHEMA_URL,
1059
+ } from '@forgezero/agent/definition';
1060
+
1061
+ export const selectedCapability = DEPLOY_SCHEMA_URL;
1062
+ ```
1063
+
811
1064
  <a id="forgezero-agent-deploy"></a>
812
1065
  ## @forgezero/agent/deploy
813
1066
 
@@ -932,6 +1185,18 @@ import type {
932
1185
  } from '@forgezero/agent/deploy-plan';
933
1186
  ```
934
1187
 
1188
+ ## @forgezero/agent/deploy-plan — Use this entry point
1189
+
1190
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1191
+
1192
+ ```text
1193
+ import {
1194
+ DEPLOY_PLAN_FORMAT,
1195
+ } from '@forgezero/agent/deploy-plan';
1196
+
1197
+ export const selectedCapability = DEPLOY_PLAN_FORMAT;
1198
+ ```
1199
+
935
1200
  <a id="forgezero-agent-deploy-plan-runner"></a>
936
1201
  ## @forgezero/agent/deploy-plan-runner
937
1202
 
@@ -1000,6 +1265,18 @@ import type {
1000
1265
  } from '@forgezero/agent/deploy-compiler';
1001
1266
  ```
1002
1267
 
1268
+ ## @forgezero/agent/deploy-compiler — Use this entry point
1269
+
1270
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1271
+
1272
+ ```text
1273
+ import {
1274
+ DEPLOY_PLAN_FILE,
1275
+ } from '@forgezero/agent/deploy-compiler';
1276
+
1277
+ export const selectedCapability = DEPLOY_PLAN_FILE;
1278
+ ```
1279
+
1003
1280
  <a id="forgezero-agent-deploy-file"></a>
1004
1281
  ## @forgezero/agent/deploy-file
1005
1282
 
@@ -1022,6 +1299,18 @@ import type {
1022
1299
  } from '@forgezero/agent/deploy-file';
1023
1300
  ```
1024
1301
 
1302
+ ## @forgezero/agent/deploy-file — Use this entry point
1303
+
1304
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1305
+
1306
+ ```text
1307
+ import {
1308
+ DEPLOY_FILE,
1309
+ } from '@forgezero/agent/deploy-file';
1310
+
1311
+ export const selectedCapability = DEPLOY_FILE;
1312
+ ```
1313
+
1025
1314
  <a id="forgezero-agent-metal-bootstrap"></a>
1026
1315
  ## @forgezero/agent/metal-bootstrap
1027
1316
 
@@ -1052,6 +1341,18 @@ import type {
1052
1341
  } from '@forgezero/agent/metal-bootstrap';
1053
1342
  ```
1054
1343
 
1344
+ ## @forgezero/agent/metal-bootstrap — Use this entry point
1345
+
1346
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1347
+
1348
+ ```text
1349
+ import {
1350
+ METAL_BOOTSTRAP_STATE_PATH,
1351
+ } from '@forgezero/agent/metal-bootstrap';
1352
+
1353
+ export const selectedCapability = METAL_BOOTSTRAP_STATE_PATH;
1354
+ ```
1355
+
1055
1356
  <a id="forgezero-agent-metal-provision"></a>
1056
1357
  ## @forgezero/agent/metal-provision
1057
1358
 
@@ -1082,6 +1383,18 @@ import type {
1082
1383
  } from '@forgezero/agent/metal-provision';
1083
1384
  ```
1084
1385
 
1386
+ ## @forgezero/agent/metal-provision — Use this entry point
1387
+
1388
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1389
+
1390
+ ```text
1391
+ import {
1392
+ MetalProvisionError,
1393
+ } from '@forgezero/agent/metal-provision';
1394
+
1395
+ export const selectedCapability = MetalProvisionError;
1396
+ ```
1397
+
1085
1398
  <a id="forgezero-agent-platform-bootstrap-runtime"></a>
1086
1399
  ## @forgezero/agent/platform-bootstrap-runtime
1087
1400
 
@@ -1119,6 +1432,18 @@ import type {
1119
1432
  } from '@forgezero/agent/platform-bootstrap-runtime';
1120
1433
  ```
1121
1434
 
1435
+ ## @forgezero/agent/platform-bootstrap-runtime — Use this entry point
1436
+
1437
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1438
+
1439
+ ```text
1440
+ import {
1441
+ activatePlatformRelease,
1442
+ } from '@forgezero/agent/platform-bootstrap-runtime';
1443
+
1444
+ export const selectedCapability = activatePlatformRelease;
1445
+ ```
1446
+
1122
1447
  <a id="forgezero-agent-project-context"></a>
1123
1448
  ## @forgezero/agent/project-context
1124
1449
 
@@ -1145,6 +1470,18 @@ import type {
1145
1470
  } from '@forgezero/agent/project-context';
1146
1471
  ```
1147
1472
 
1473
+ ## @forgezero/agent/project-context — Use this entry point
1474
+
1475
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1476
+
1477
+ ```text
1478
+ import {
1479
+ PROJECT_CONTEXT_VERSION,
1480
+ } from '@forgezero/agent/project-context';
1481
+
1482
+ export const selectedCapability = PROJECT_CONTEXT_VERSION;
1483
+ ```
1484
+
1148
1485
  <a id="forgezero-agent-provisioning-pull"></a>
1149
1486
  ## @forgezero/agent/provisioning-pull
1150
1487
 
@@ -1167,6 +1504,18 @@ import type {
1167
1504
  } from '@forgezero/agent/provisioning-pull';
1168
1505
  ```
1169
1506
 
1507
+ ## @forgezero/agent/provisioning-pull — Use this entry point
1508
+
1509
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1510
+
1511
+ ```text
1512
+ import {
1513
+ pullProvisioningOnce,
1514
+ } from '@forgezero/agent/provisioning-pull';
1515
+
1516
+ export const selectedCapability = pullProvisioningOnce;
1517
+ ```
1518
+
1170
1519
  <a id="forgezero-agent-schema-deploy-v3-json"></a>
1171
1520
  ## @forgezero/agent/schema/deploy-v3.json
1172
1521
 
@@ -1221,6 +1570,18 @@ import type {
1221
1570
  } from '@forgezero/agent/software';
1222
1571
  ```
1223
1572
 
1573
+ ## @forgezero/agent/software — Use this entry point
1574
+
1575
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1576
+
1577
+ ```text
1578
+ import {
1579
+ BUN_RELEASE_SHA256,
1580
+ } from '@forgezero/agent/software';
1581
+
1582
+ export const selectedCapability = BUN_RELEASE_SHA256;
1583
+ ```
1584
+
1224
1585
  <a id="forgezero-agent-software-helper"></a>
1225
1586
  ## @forgezero/agent/software-helper
1226
1587
 
@@ -1239,6 +1600,18 @@ import {
1239
1600
  } from '@forgezero/agent/software-helper';
1240
1601
  ```
1241
1602
 
1603
+ ## @forgezero/agent/software-helper — Use this entry point
1604
+
1605
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1606
+
1607
+ ```text
1608
+ import {
1609
+ DEFAULT_SOFTWARE_HELPER_SOCKET,
1610
+ } from '@forgezero/agent/software-helper';
1611
+
1612
+ export const selectedCapability = DEFAULT_SOFTWARE_HELPER_SOCKET;
1613
+ ```
1614
+
1242
1615
  <a id="forgezero-agent-ubuntu"></a>
1243
1616
  ## @forgezero/agent/ubuntu
1244
1617
 
@@ -1253,13 +1626,35 @@ import {
1253
1626
  } from '@forgezero/agent/ubuntu';
1254
1627
  ```
1255
1628
 
1629
+ ## @forgezero/agent/ubuntu — Use this entry point
1630
+
1631
+ This minimal executable use imports one concrete value from this exact entry point without a wildcard or package-root detour. Keep the value or values the application actually needs; the full named inventory remains directly above it.
1632
+
1633
+ ```text
1634
+ import {
1635
+ SUPPORTED_GUEST_IMAGE,
1636
+ } from '@forgezero/agent/ubuntu';
1637
+
1638
+ export const selectedCapability = SUPPORTED_GUEST_IMAGE;
1639
+ ```
1640
+
1256
1641
  <a id="fz-agent-replica"></a>
1257
1642
  ## fz-agent (replica)
1258
1643
 
1259
- 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. Run the command with `--help` before applying it.
1644
+ 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. This is a managed runtime interface label, not an import or standalone executable.
1645
+
1646
+ ```text
1647
+ fz-agent (replica)
1648
+ # Accessed through the installed Agent boundary.
1649
+ ```
1650
+
1651
+ ## fz-agent (replica) — Operational interface
1652
+
1653
+ This inventory entry names an Agent-owned runtime interface, not an importable namespace or a command to paste. Applications reach it through the installed Agent and the documented Vault client.
1260
1654
 
1261
1655
  ```text
1262
- fz-agent (replica) --help
1656
+ fz-agent (replica)
1657
+ # Managed by the installed fz-agent service; no direct executable entry.
1263
1658
  ```
1264
1659
 
1265
1660
  ## Install both machine commands
@@ -749,7 +749,7 @@ async function postSignedNode(options, path, body) {
749
749
  }
750
750
 
751
751
  // src/version.ts
752
- var VERSION3 = "0.1.72";
752
+ var VERSION3 = "0.1.74";
753
753
 
754
754
  // src/agent-heartbeat.ts
755
755
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
@@ -35,6 +35,8 @@ export interface PlatformBootstrapConfig {
35
35
  role: DatabaseBootstrapRole;
36
36
  /** Genesis master/joiners are Agency members; elastic joiners normally use `none`. */
37
37
  agency: DatabaseAgencyParticipation;
38
+ /** Prefer this normal writable Coordinator only for explicitly read-safe operations. */
39
+ readPreferred?: boolean;
38
40
  /** Cluster mode must remain default. Bootstrap verifies it and never mutates it. */
39
41
  serverMode: 'default';
40
42
  address?: string;
package/dist/bootstrap.js CHANGED
@@ -1416,7 +1416,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1416
1416
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1417
1417
 
1418
1418
  // src/version.ts
1419
- var VERSION = "0.1.72";
1419
+ var VERSION = "0.1.74";
1420
1420
 
1421
1421
  // src/software.ts
1422
1422
  var PINNED_BUN_VERSION = "1.3.14";
@@ -3060,6 +3060,10 @@ function validatePlatformSharedEnvironment(input) {
3060
3060
  const coordinators = input.databaseCoordinators.map(privateCoordinator);
3061
3061
  if (new Set(coordinators).size !== coordinators.length)
3062
3062
  throw new Error("databaseCoordinators must be unique.");
3063
+ const readPreferred = (input.databaseReadPreferredCoordinators ?? []).map(privateCoordinator);
3064
+ if (new Set(readPreferred).size !== readPreferred.length || readPreferred.some((url) => !coordinators.includes(url))) {
3065
+ throw new Error("databaseReadPreferredCoordinators must be a unique subset of databaseCoordinators.");
3066
+ }
3063
3067
  if (input.databaseNetworkMode !== "private-lan") {
3064
3068
  throw new Error("Attended platform bootstrap supports only private-lan database networking.");
3065
3069
  }
@@ -3133,6 +3137,7 @@ function validatePlatformSharedEnvironment(input) {
3133
3137
  return {
3134
3138
  ...input,
3135
3139
  databaseCoordinators: coordinators,
3140
+ databaseReadPreferredCoordinators: readPreferred,
3136
3141
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
3137
3142
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
3138
3143
  agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
@@ -3146,6 +3151,8 @@ function renderPlatformSharedEnvironment(input) {
3146
3151
  const entries = {
3147
3152
  ARANGO_URL: value.databaseCoordinators[0],
3148
3153
  ARANGO_URLS: value.databaseCoordinators.join(","),
3154
+ ARANGO_READ_PREFERRED_URLS: (value.databaseReadPreferredCoordinators ?? []).join(","),
3155
+ ARANGO_READ_PREFERRED_FALLBACK: "balanced",
3149
3156
  ARANGO_DB: "fz",
3150
3157
  FZ_DATABASE_MODE: "platform",
3151
3158
  ARANGO_USER: value.databaseUser,
@@ -3769,6 +3776,10 @@ function validateBootstrapConfig(value) {
3769
3776
  if (!["member", "none"].includes(value.database.agency) || value.database.role === "master" && value.database.agency !== "member" || value.database.role === "none" && value.database.agency !== "none") {
3770
3777
  throw new Error("database role and Agency participation disagree");
3771
3778
  }
3779
+ value.database.readPreferred ??= false;
3780
+ if (value.database.readPreferred && (value.database.role !== "joiner" || value.database.agency !== "none")) {
3781
+ throw new Error("read-preferred database routing requires an extra non-Agency joiner");
3782
+ }
3772
3783
  if (value.database.role !== "none" && !value.database.address)
3773
3784
  throw new Error("database nodes require a private address");
3774
3785
  if (value.database.role === "joiner" && !value.database.master)
@@ -3788,6 +3799,10 @@ function validateBootstrapConfig(value) {
3788
3799
  if (runtime.softwareProfile !== value.profile || runtime.databaseRole !== value.database.role || runtime.nodeHostname !== value.nodeHostname || runtime.apiOrigin !== api.origin || runtime.databaseCoordinators.join(",") !== write.join(",")) {
3789
3800
  throw new Error("platform runtime coordinates disagree with immutable bootstrap coordinates");
3790
3801
  }
3802
+ const ownCoordinator = value.database.address ? `http://${value.database.address}:8529` : undefined;
3803
+ if (Boolean(ownCoordinator && (runtime.databaseReadPreferredCoordinators ?? []).includes(ownCoordinator)) !== value.database.readPreferred) {
3804
+ throw new Error("runtime read-preferred coordinator set disagrees with this node database routing intent");
3805
+ }
3791
3806
  if (runtime.deployProfile !== value.environment)
3792
3807
  throw new Error("runtime deployment profile disagrees with bootstrap environment");
3793
3808
  if (value.database.address !== runtime.databaseAddress || value.database.master !== runtime.databaseMaster) {
@@ -4099,6 +4114,7 @@ function bootstrapIdentity(config) {
4099
4114
  role: config.database.role,
4100
4115
  serverMode: config.database.serverMode,
4101
4116
  agency: config.database.agency,
4117
+ readPreferred: config.database.readPreferred,
4102
4118
  address: config.database.address ?? null,
4103
4119
  master: config.database.master ?? null,
4104
4120
  coordinators: config.database.coordinators
@@ -4134,7 +4150,7 @@ function parseStoredState(raw) {
4134
4150
  if (!value || typeof value !== "object" || Array.isArray(value))
4135
4151
  throw new Error("bootstrap state is malformed");
4136
4152
  const state = value;
4137
- if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && !["member", "none"].includes(state.databaseAgency ?? "")) {
4153
+ if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && (!["member", "none"].includes(state.databaseAgency ?? "") || typeof state.databaseReadPreferred !== "boolean")) {
4138
4154
  throw new Error("bootstrap state is legacy or incomplete; refusing an unbound repair");
4139
4155
  }
4140
4156
  return state;
@@ -4184,6 +4200,7 @@ function stateFor(config, cloudflare, previousCloudflareTunnelId) {
4184
4200
  environment: config.environment,
4185
4201
  databaseRole: config.database.role,
4186
4202
  databaseAgency: config.database.agency,
4203
+ databaseReadPreferred: config.database.readPreferred,
4187
4204
  databaseServerMode: config.database.serverMode,
4188
4205
  databaseAddress: config.database.address,
4189
4206
  databaseCoordinators: config.database.coordinators,
@@ -4688,7 +4705,7 @@ function strictBootstrapDocument(value) {
4688
4705
  exactKeys(root.firewall, ["enabled", "sshPort", "privateCidrs"], "firewall config");
4689
4706
  if (root.cloudflareHandoff !== undefined)
4690
4707
  exactKeys(root.cloudflareHandoff, ["handoffFile", "nodeName"], "Cloudflare handoff");
4691
- exactKeys(root.database, ["role", "agency", "serverMode", "address", "master", "coordinators"], "database config");
4708
+ exactKeys(root.database, ["role", "agency", "readPreferred", "serverMode", "address", "master", "coordinators"], "database config");
4692
4709
  exactKeys(root.enrolment, ["source"], "platform enrolment config");
4693
4710
  const runtime = exactKeys(root.runtime, [
4694
4711
  "environment",
@@ -4732,7 +4749,8 @@ function strictBootstrapDocument(value) {
4732
4749
  "backup",
4733
4750
  "cloudflare",
4734
4751
  "realtime",
4735
- "initialInventory"
4752
+ "initialInventory",
4753
+ "databaseReadPreferredCoordinators"
4736
4754
  ], "runtime environment");
4737
4755
  const environment = runtime.environment;
4738
4756
  if (environment.initialInventory !== undefined) {
package/dist/fz-agent.js CHANGED
@@ -8588,7 +8588,7 @@ import { join as join3 } from "path";
8588
8588
  import { fileURLToPath } from "url";
8589
8589
 
8590
8590
  // src/version.ts
8591
- var VERSION2 = "0.1.72";
8591
+ var VERSION2 = "0.1.74";
8592
8592
 
8593
8593
  // src/ssh-bootstrap.ts
8594
8594
  class SshBootstrapError extends Error {
@@ -14913,7 +14913,7 @@ function parseStoredState(raw) {
14913
14913
  if (!value || typeof value !== "object" || Array.isArray(value))
14914
14914
  throw new Error("bootstrap state is malformed");
14915
14915
  const state = value;
14916
- if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && !["member", "none"].includes(state.databaseAgency ?? "")) {
14916
+ if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && (!["member", "none"].includes(state.databaseAgency ?? "") || typeof state.databaseReadPreferred !== "boolean")) {
14917
14917
  throw new Error("bootstrap state is legacy or incomplete; refusing an unbound repair");
14918
14918
  }
14919
14919
  return state;
package/dist/fz.js CHANGED
@@ -4634,7 +4634,7 @@ var VaultError, runtimeEnvironment = () => typeof process !== "undefined" && pro
4634
4634
  } catch {
4635
4635
  return;
4636
4636
  }
4637
- }, VERSION = "0.1.13";
4637
+ }, VERSION = "0.1.14";
4638
4638
  var init_dist = __esm(() => {
4639
4639
  init_identity();
4640
4640
  VaultError = class VaultError extends Error {
@@ -4829,7 +4829,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
4829
4829
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
4830
4830
 
4831
4831
  // src/version.ts
4832
- var VERSION2 = "0.1.72";
4832
+ var VERSION2 = "0.1.74";
4833
4833
 
4834
4834
  // src/software.ts
4835
4835
  var PINNED_BUN_VERSION = "1.3.14";
@@ -11843,6 +11843,10 @@ function validatePlatformSharedEnvironment(input) {
11843
11843
  const coordinators = input.databaseCoordinators.map(privateCoordinator);
11844
11844
  if (new Set(coordinators).size !== coordinators.length)
11845
11845
  throw new Error("databaseCoordinators must be unique.");
11846
+ const readPreferred = (input.databaseReadPreferredCoordinators ?? []).map(privateCoordinator);
11847
+ if (new Set(readPreferred).size !== readPreferred.length || readPreferred.some((url) => !coordinators.includes(url))) {
11848
+ throw new Error("databaseReadPreferredCoordinators must be a unique subset of databaseCoordinators.");
11849
+ }
11846
11850
  if (input.databaseNetworkMode !== "private-lan") {
11847
11851
  throw new Error("Attended platform bootstrap supports only private-lan database networking.");
11848
11852
  }
@@ -11916,6 +11920,7 @@ function validatePlatformSharedEnvironment(input) {
11916
11920
  return {
11917
11921
  ...input,
11918
11922
  databaseCoordinators: coordinators,
11923
+ databaseReadPreferredCoordinators: readPreferred,
11919
11924
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
11920
11925
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
11921
11926
  agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
@@ -11929,6 +11934,8 @@ function renderPlatformSharedEnvironment(input) {
11929
11934
  const entries = {
11930
11935
  ARANGO_URL: value.databaseCoordinators[0],
11931
11936
  ARANGO_URLS: value.databaseCoordinators.join(","),
11937
+ ARANGO_READ_PREFERRED_URLS: (value.databaseReadPreferredCoordinators ?? []).join(","),
11938
+ ARANGO_READ_PREFERRED_FALLBACK: "balanced",
11932
11939
  ARANGO_DB: "fz",
11933
11940
  FZ_DATABASE_MODE: "platform",
11934
11941
  ARANGO_USER: value.databaseUser,
@@ -13788,6 +13795,10 @@ function validateBootstrapConfig(value) {
13788
13795
  if (!["member", "none"].includes(value.database.agency) || value.database.role === "master" && value.database.agency !== "member" || value.database.role === "none" && value.database.agency !== "none") {
13789
13796
  throw new Error("database role and Agency participation disagree");
13790
13797
  }
13798
+ value.database.readPreferred ??= false;
13799
+ if (value.database.readPreferred && (value.database.role !== "joiner" || value.database.agency !== "none")) {
13800
+ throw new Error("read-preferred database routing requires an extra non-Agency joiner");
13801
+ }
13791
13802
  if (value.database.role !== "none" && !value.database.address)
13792
13803
  throw new Error("database nodes require a private address");
13793
13804
  if (value.database.role === "joiner" && !value.database.master)
@@ -13807,6 +13818,10 @@ function validateBootstrapConfig(value) {
13807
13818
  if (runtime.softwareProfile !== value.profile || runtime.databaseRole !== value.database.role || runtime.nodeHostname !== value.nodeHostname || runtime.apiOrigin !== api.origin || runtime.databaseCoordinators.join(",") !== write.join(",")) {
13808
13819
  throw new Error("platform runtime coordinates disagree with immutable bootstrap coordinates");
13809
13820
  }
13821
+ const ownCoordinator = value.database.address ? `http://${value.database.address}:8529` : undefined;
13822
+ if (Boolean(ownCoordinator && (runtime.databaseReadPreferredCoordinators ?? []).includes(ownCoordinator)) !== value.database.readPreferred) {
13823
+ throw new Error("runtime read-preferred coordinator set disagrees with this node database routing intent");
13824
+ }
13810
13825
  if (runtime.deployProfile !== value.environment)
13811
13826
  throw new Error("runtime deployment profile disagrees with bootstrap environment");
13812
13827
  if (value.database.address !== runtime.databaseAddress || value.database.master !== runtime.databaseMaster) {
@@ -14118,6 +14133,7 @@ function bootstrapIdentity(config) {
14118
14133
  role: config.database.role,
14119
14134
  serverMode: config.database.serverMode,
14120
14135
  agency: config.database.agency,
14136
+ readPreferred: config.database.readPreferred,
14121
14137
  address: config.database.address ?? null,
14122
14138
  master: config.database.master ?? null,
14123
14139
  coordinators: config.database.coordinators
@@ -14153,7 +14169,7 @@ function parseStoredState(raw) {
14153
14169
  if (!value || typeof value !== "object" || Array.isArray(value))
14154
14170
  throw new Error("bootstrap state is malformed");
14155
14171
  const state = value;
14156
- if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && !["member", "none"].includes(state.databaseAgency ?? "")) {
14172
+ if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && (!["member", "none"].includes(state.databaseAgency ?? "") || typeof state.databaseReadPreferred !== "boolean")) {
14157
14173
  throw new Error("bootstrap state is legacy or incomplete; refusing an unbound repair");
14158
14174
  }
14159
14175
  return state;
@@ -14203,6 +14219,7 @@ function stateFor(config, cloudflare, previousCloudflareTunnelId) {
14203
14219
  environment: config.environment,
14204
14220
  databaseRole: config.database.role,
14205
14221
  databaseAgency: config.database.agency,
14222
+ databaseReadPreferred: config.database.readPreferred,
14206
14223
  databaseServerMode: config.database.serverMode,
14207
14224
  databaseAddress: config.database.address,
14208
14225
  databaseCoordinators: config.database.coordinators,
@@ -14707,7 +14724,7 @@ function strictBootstrapDocument(value) {
14707
14724
  exactKeys2(root.firewall, ["enabled", "sshPort", "privateCidrs"], "firewall config");
14708
14725
  if (root.cloudflareHandoff !== undefined)
14709
14726
  exactKeys2(root.cloudflareHandoff, ["handoffFile", "nodeName"], "Cloudflare handoff");
14710
- exactKeys2(root.database, ["role", "agency", "serverMode", "address", "master", "coordinators"], "database config");
14727
+ exactKeys2(root.database, ["role", "agency", "readPreferred", "serverMode", "address", "master", "coordinators"], "database config");
14711
14728
  exactKeys2(root.enrolment, ["source"], "platform enrolment config");
14712
14729
  const runtime = exactKeys2(root.runtime, [
14713
14730
  "environment",
@@ -14751,7 +14768,8 @@ function strictBootstrapDocument(value) {
14751
14768
  "backup",
14752
14769
  "cloudflare",
14753
14770
  "realtime",
14754
- "initialInventory"
14771
+ "initialInventory",
14772
+ "databaseReadPreferredCoordinators"
14755
14773
  ], "runtime environment");
14756
14774
  const environment = runtime.environment;
14757
14775
  if (environment.initialInventory !== undefined) {
@@ -17073,6 +17091,7 @@ function platformGenesisBootstrapConfigs(template, guests, nodes, metalHostname,
17073
17091
  config.database = {
17074
17092
  role,
17075
17093
  agency: "member",
17094
+ readPreferred: false,
17076
17095
  serverMode: "default",
17077
17096
  address: guest.address,
17078
17097
  ...role === "joiner" ? { master } : {},
@@ -17087,6 +17106,7 @@ function platformGenesisBootstrapConfigs(template, guests, nodes, metalHostname,
17087
17106
  softwareProfile: "platform-db-api",
17088
17107
  databaseRole: role,
17089
17108
  databaseCoordinators: coordinators,
17109
+ databaseReadPreferredCoordinators: [],
17090
17110
  databaseAddress: guest.address,
17091
17111
  databaseMaster: role === "joiner" ? master : undefined,
17092
17112
  nodeHostname: node.nodeHostname,
@@ -17418,6 +17438,7 @@ function parseOptions(argv2) {
17418
17438
  realm: process.env.FZ_REALM ?? "platform",
17419
17439
  realmExplicit: Boolean(process.env.FZ_REALM),
17420
17440
  json: false,
17441
+ help: false,
17421
17442
  apply: false,
17422
17443
  enrol: false,
17423
17444
  socket: process.env.SSH_AUTH_SOCK,
@@ -17456,6 +17477,8 @@ function parseOptions(argv2) {
17456
17477
  options.socket = argv2[++index];
17457
17478
  else if (token === "--json")
17458
17479
  options.json = true;
17480
+ else if (token === "--help" || token === "-h")
17481
+ options.help = true;
17459
17482
  else if (token === "--apply")
17460
17483
  options.apply = true;
17461
17484
  else if (token === "--enrol")
@@ -18416,6 +18439,7 @@ function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint)
18416
18439
  const profile = genesis ? "platform-db-api" : bootstrapAnswer("Profile (platform-db-api/platform-api)", "platform-db-api");
18417
18440
  const role = genesis ? "master" : profile === "platform-api" ? "none" : bootstrapAnswer("Database role (master/joiner)", "master");
18418
18441
  const agency = genesis ? "member" : role === "master" ? "member" : role === "none" ? "none" : bootstrapAnswer("Agency participation (member/none)", "member");
18442
+ const readPreferred = !genesis && role === "joiner" && agency === "none" ? bootstrapBoolean("Prefer this Coordinator for explicitly read-safe queries?", "no") : false;
18419
18443
  const serverMode = "default";
18420
18444
  const address = genesis ? fleet[0].address : role === "none" ? undefined : bootstrapAnswer("Private database address");
18421
18445
  const master = role === "joiner" ? bootstrapAnswer("Master starter private address") : undefined;
@@ -18460,6 +18484,7 @@ function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint)
18460
18484
  database: {
18461
18485
  role,
18462
18486
  agency,
18487
+ readPreferred,
18463
18488
  serverMode,
18464
18489
  address,
18465
18490
  master,
@@ -18471,6 +18496,7 @@ function interactiveBootstrap(kind, genesisGuests, genesisCloudflareCheckpoint)
18471
18496
  softwareProfile: profile,
18472
18497
  databaseRole: role,
18473
18498
  databaseCoordinators: coordinators,
18499
+ databaseReadPreferredCoordinators: readPreferred && address ? [`http://${address}:8529`] : [],
18474
18500
  databaseAddress: address,
18475
18501
  databaseMaster: master,
18476
18502
  databaseNetworkMode,
@@ -19473,6 +19499,10 @@ if (import.meta.main) {
19473
19499
  }
19474
19500
  async function runCli() {
19475
19501
  const { command, args, options } = parseOptions(process.argv.slice(2));
19502
+ if (options.help) {
19503
+ usage();
19504
+ process.exit(0);
19505
+ }
19476
19506
  try {
19477
19507
  hydrateSession(options);
19478
19508
  } catch (cause) {
@@ -354,7 +354,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
354
354
  }
355
355
 
356
356
  // src/version.ts
357
- var VERSION = "0.1.72";
357
+ var VERSION = "0.1.74";
358
358
 
359
359
  // src/otel-collector.ts
360
360
  var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
@@ -1416,7 +1416,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
1416
1416
  var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
1417
1417
 
1418
1418
  // src/version.ts
1419
- var VERSION = "0.1.72";
1419
+ var VERSION = "0.1.74";
1420
1420
 
1421
1421
  // src/software.ts
1422
1422
  var PINNED_BUN_VERSION = "1.3.14";
@@ -3060,6 +3060,10 @@ function validatePlatformSharedEnvironment(input) {
3060
3060
  const coordinators = input.databaseCoordinators.map(privateCoordinator);
3061
3061
  if (new Set(coordinators).size !== coordinators.length)
3062
3062
  throw new Error("databaseCoordinators must be unique.");
3063
+ const readPreferred = (input.databaseReadPreferredCoordinators ?? []).map(privateCoordinator);
3064
+ if (new Set(readPreferred).size !== readPreferred.length || readPreferred.some((url) => !coordinators.includes(url))) {
3065
+ throw new Error("databaseReadPreferredCoordinators must be a unique subset of databaseCoordinators.");
3066
+ }
3063
3067
  if (input.databaseNetworkMode !== "private-lan") {
3064
3068
  throw new Error("Attended platform bootstrap supports only private-lan database networking.");
3065
3069
  }
@@ -3133,6 +3137,7 @@ function validatePlatformSharedEnvironment(input) {
3133
3137
  return {
3134
3138
  ...input,
3135
3139
  databaseCoordinators: coordinators,
3140
+ databaseReadPreferredCoordinators: readPreferred,
3136
3141
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
3137
3142
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
3138
3143
  agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
@@ -3146,6 +3151,8 @@ function renderPlatformSharedEnvironment(input) {
3146
3151
  const entries = {
3147
3152
  ARANGO_URL: value.databaseCoordinators[0],
3148
3153
  ARANGO_URLS: value.databaseCoordinators.join(","),
3154
+ ARANGO_READ_PREFERRED_URLS: (value.databaseReadPreferredCoordinators ?? []).join(","),
3155
+ ARANGO_READ_PREFERRED_FALLBACK: "balanced",
3149
3156
  ARANGO_DB: "fz",
3150
3157
  FZ_DATABASE_MODE: "platform",
3151
3158
  ARANGO_USER: value.databaseUser,
@@ -3769,6 +3776,10 @@ function validateBootstrapConfig(value) {
3769
3776
  if (!["member", "none"].includes(value.database.agency) || value.database.role === "master" && value.database.agency !== "member" || value.database.role === "none" && value.database.agency !== "none") {
3770
3777
  throw new Error("database role and Agency participation disagree");
3771
3778
  }
3779
+ value.database.readPreferred ??= false;
3780
+ if (value.database.readPreferred && (value.database.role !== "joiner" || value.database.agency !== "none")) {
3781
+ throw new Error("read-preferred database routing requires an extra non-Agency joiner");
3782
+ }
3772
3783
  if (value.database.role !== "none" && !value.database.address)
3773
3784
  throw new Error("database nodes require a private address");
3774
3785
  if (value.database.role === "joiner" && !value.database.master)
@@ -3788,6 +3799,10 @@ function validateBootstrapConfig(value) {
3788
3799
  if (runtime.softwareProfile !== value.profile || runtime.databaseRole !== value.database.role || runtime.nodeHostname !== value.nodeHostname || runtime.apiOrigin !== api.origin || runtime.databaseCoordinators.join(",") !== write.join(",")) {
3789
3800
  throw new Error("platform runtime coordinates disagree with immutable bootstrap coordinates");
3790
3801
  }
3802
+ const ownCoordinator = value.database.address ? `http://${value.database.address}:8529` : undefined;
3803
+ if (Boolean(ownCoordinator && (runtime.databaseReadPreferredCoordinators ?? []).includes(ownCoordinator)) !== value.database.readPreferred) {
3804
+ throw new Error("runtime read-preferred coordinator set disagrees with this node database routing intent");
3805
+ }
3791
3806
  if (runtime.deployProfile !== value.environment)
3792
3807
  throw new Error("runtime deployment profile disagrees with bootstrap environment");
3793
3808
  if (value.database.address !== runtime.databaseAddress || value.database.master !== runtime.databaseMaster) {
@@ -4099,6 +4114,7 @@ function bootstrapIdentity(config) {
4099
4114
  role: config.database.role,
4100
4115
  serverMode: config.database.serverMode,
4101
4116
  agency: config.database.agency,
4117
+ readPreferred: config.database.readPreferred,
4102
4118
  address: config.database.address ?? null,
4103
4119
  master: config.database.master ?? null,
4104
4120
  coordinators: config.database.coordinators
@@ -4134,7 +4150,7 @@ function parseStoredState(raw) {
4134
4150
  if (!value || typeof value !== "object" || Array.isArray(value))
4135
4151
  throw new Error("bootstrap state is malformed");
4136
4152
  const state = value;
4137
- if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && !["member", "none"].includes(state.databaseAgency ?? "")) {
4153
+ if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && (!["member", "none"].includes(state.databaseAgency ?? "") || typeof state.databaseReadPreferred !== "boolean")) {
4138
4154
  throw new Error("bootstrap state is legacy or incomplete; refusing an unbound repair");
4139
4155
  }
4140
4156
  return state;
@@ -4184,6 +4200,7 @@ function stateFor(config, cloudflare, previousCloudflareTunnelId) {
4184
4200
  environment: config.environment,
4185
4201
  databaseRole: config.database.role,
4186
4202
  databaseAgency: config.database.agency,
4203
+ databaseReadPreferred: config.database.readPreferred,
4187
4204
  databaseServerMode: config.database.serverMode,
4188
4205
  databaseAddress: config.database.address,
4189
4206
  databaseCoordinators: config.database.coordinators,
@@ -4688,7 +4705,7 @@ function strictBootstrapDocument(value) {
4688
4705
  exactKeys(root.firewall, ["enabled", "sshPort", "privateCidrs"], "firewall config");
4689
4706
  if (root.cloudflareHandoff !== undefined)
4690
4707
  exactKeys(root.cloudflareHandoff, ["handoffFile", "nodeName"], "Cloudflare handoff");
4691
- exactKeys(root.database, ["role", "agency", "serverMode", "address", "master", "coordinators"], "database config");
4708
+ exactKeys(root.database, ["role", "agency", "readPreferred", "serverMode", "address", "master", "coordinators"], "database config");
4692
4709
  exactKeys(root.enrolment, ["source"], "platform enrolment config");
4693
4710
  const runtime = exactKeys(root.runtime, [
4694
4711
  "environment",
@@ -4732,7 +4749,8 @@ function strictBootstrapDocument(value) {
4732
4749
  "backup",
4733
4750
  "cloudflare",
4734
4751
  "realtime",
4735
- "initialInventory"
4752
+ "initialInventory",
4753
+ "databaseReadPreferredCoordinators"
4736
4754
  ], "runtime environment");
4737
4755
  const environment = runtime.environment;
4738
4756
  if (environment.initialInventory !== undefined) {
@@ -47,6 +47,7 @@ export interface PlatformSharedEnvironment {
47
47
  softwareProfile: PlatformSoftwareProfile;
48
48
  databaseRole: PlatformDatabaseRole;
49
49
  databaseCoordinators: string[];
50
+ databaseReadPreferredCoordinators?: string[];
50
51
  databaseAddress?: string;
51
52
  databaseMaster?: string;
52
53
  databaseNetworkMode: 'private-lan';
@@ -565,6 +565,10 @@ function validatePlatformSharedEnvironment(input) {
565
565
  const coordinators = input.databaseCoordinators.map(privateCoordinator);
566
566
  if (new Set(coordinators).size !== coordinators.length)
567
567
  throw new Error("databaseCoordinators must be unique.");
568
+ const readPreferred = (input.databaseReadPreferredCoordinators ?? []).map(privateCoordinator);
569
+ if (new Set(readPreferred).size !== readPreferred.length || readPreferred.some((url) => !coordinators.includes(url))) {
570
+ throw new Error("databaseReadPreferredCoordinators must be a unique subset of databaseCoordinators.");
571
+ }
568
572
  if (input.databaseNetworkMode !== "private-lan") {
569
573
  throw new Error("Attended platform bootstrap supports only private-lan database networking.");
570
574
  }
@@ -638,6 +642,7 @@ function validatePlatformSharedEnvironment(input) {
638
642
  return {
639
643
  ...input,
640
644
  databaseCoordinators: coordinators,
645
+ databaseReadPreferredCoordinators: readPreferred,
641
646
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
642
647
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
643
648
  agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
@@ -651,6 +656,8 @@ function renderPlatformSharedEnvironment(input) {
651
656
  const entries = {
652
657
  ARANGO_URL: value.databaseCoordinators[0],
653
658
  ARANGO_URLS: value.databaseCoordinators.join(","),
659
+ ARANGO_READ_PREFERRED_URLS: (value.databaseReadPreferredCoordinators ?? []).join(","),
660
+ ARANGO_READ_PREFERRED_FALLBACK: "balanced",
654
661
  ARANGO_DB: "fz",
655
662
  FZ_DATABASE_MODE: "platform",
656
663
  ARANGO_USER: value.databaseUser,
@@ -2125,7 +2125,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2125
2125
  }
2126
2126
 
2127
2127
  // src/version.ts
2128
- var VERSION3 = "0.1.72";
2128
+ var VERSION3 = "0.1.74";
2129
2129
 
2130
2130
  // src/egress-policy.ts
2131
2131
  import { realpathSync as realpathSync3 } from "node:fs";
@@ -3360,6 +3360,10 @@ function validatePlatformSharedEnvironment(input) {
3360
3360
  const coordinators = input.databaseCoordinators.map(privateCoordinator);
3361
3361
  if (new Set(coordinators).size !== coordinators.length)
3362
3362
  throw new Error("databaseCoordinators must be unique.");
3363
+ const readPreferred = (input.databaseReadPreferredCoordinators ?? []).map(privateCoordinator);
3364
+ if (new Set(readPreferred).size !== readPreferred.length || readPreferred.some((url) => !coordinators.includes(url))) {
3365
+ throw new Error("databaseReadPreferredCoordinators must be a unique subset of databaseCoordinators.");
3366
+ }
3363
3367
  if (input.databaseNetworkMode !== "private-lan") {
3364
3368
  throw new Error("Attended platform bootstrap supports only private-lan database networking.");
3365
3369
  }
@@ -3433,6 +3437,7 @@ function validatePlatformSharedEnvironment(input) {
3433
3437
  return {
3434
3438
  ...input,
3435
3439
  databaseCoordinators: coordinators,
3440
+ databaseReadPreferredCoordinators: readPreferred,
3436
3441
  appOrigin: httpsOrigin("appOrigin", input.appOrigin),
3437
3442
  apiOrigin: httpsOrigin("apiOrigin", input.apiOrigin),
3438
3443
  agentOtlpEndpoint: httpsOrigin("agentOtlpEndpoint", input.agentOtlpEndpoint),
@@ -3446,6 +3451,8 @@ function renderPlatformSharedEnvironment(input) {
3446
3451
  const entries = {
3447
3452
  ARANGO_URL: value.databaseCoordinators[0],
3448
3453
  ARANGO_URLS: value.databaseCoordinators.join(","),
3454
+ ARANGO_READ_PREFERRED_URLS: (value.databaseReadPreferredCoordinators ?? []).join(","),
3455
+ ARANGO_READ_PREFERRED_FALLBACK: "balanced",
3449
3456
  ARANGO_DB: "fz",
3450
3457
  FZ_DATABASE_MODE: "platform",
3451
3458
  ARANGO_USER: value.databaseUser,
@@ -4273,7 +4280,7 @@ function parseStoredState(raw) {
4273
4280
  if (!value || typeof value !== "object" || Array.isArray(value))
4274
4281
  throw new Error("bootstrap state is malformed");
4275
4282
  const state = value;
4276
- if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && !["member", "none"].includes(state.databaseAgency ?? "")) {
4283
+ if (state.format !== 2 || !["platform", "enrolled-compute"].includes(state.kind ?? "") || !/^[a-f0-9]{64}$/.test(state.identityDigest ?? "") || typeof state.profile !== "string" || typeof state.nodeHostname !== "string" || typeof state.apiUrl !== "string" || state.kind === "platform" && (!["member", "none"].includes(state.databaseAgency ?? "") || typeof state.databaseReadPreferred !== "boolean")) {
4277
4284
  throw new Error("bootstrap state is legacy or incomplete; refusing an unbound repair");
4278
4285
  }
4279
4286
  return state;
package/dist/provision.js CHANGED
@@ -2125,7 +2125,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
2125
2125
  }
2126
2126
 
2127
2127
  // src/version.ts
2128
- var VERSION3 = "0.1.72";
2128
+ var VERSION3 = "0.1.74";
2129
2129
 
2130
2130
  // src/egress-policy.ts
2131
2131
  import { realpathSync as realpathSync3 } from "node:fs";
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** One package version shared by both public binaries. Pinned to package.json by tests. */
2
- export declare const VERSION = "0.1.72";
2
+ export declare const VERSION = "0.1.74";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.72",
3
+ "version": "0.1.74",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",
@@ -11,13 +11,13 @@
11
11
  "typescript": "^5.6.0",
12
12
  "@types/bun": "latest",
13
13
  "@types/node": "^22.0.0",
14
- "@forgezero/access": "0.1.5",
14
+ "@forgezero/access": "0.1.6",
15
15
  "@noble/curves": "^2.2.0",
16
16
  "@noble/post-quantum": "^0.6.1"
17
17
  },
18
18
  "dependencies": {
19
- "@forgezero/runtime": "0.1.9",
20
- "@forgezero/vault": "0.1.13",
19
+ "@forgezero/runtime": "0.1.10",
20
+ "@forgezero/vault": "0.1.14",
21
21
  "@noble/curves": "2.2.0",
22
22
  "@noble/hashes": "2.2.0",
23
23
  "@noble/post-quantum": "0.6.1",