@forgezero/agent 0.1.39 → 0.1.40

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/dist/index.d.ts CHANGED
@@ -2,11 +2,14 @@
2
2
  import { type NodeKeyPair } from '@forgezero/runtime/identity';
3
3
  import { startAgent, type AgentOptions, type AttestationSource } from './socket';
4
4
  import type { SecretCache } from './cache';
5
+ import { type DeploymentCredentialSchema } from './credential-schema';
5
6
  export { VERSION } from './version';
6
7
  export { startAgent, handleRequest, handleApplicationRequest } from './socket';
7
8
  export type { AgentOptions, AttestationSource, Request, Response } from './socket';
8
9
  export { createSecretCache, CacheError } from './cache';
9
10
  export type { SecretCache, CacheOptions } from './cache';
11
+ export { AGENT_CREDENTIAL_LOCATIONS, AGENT_CREDENTIAL_POLICY, METAL_SYSTEMD_CREDENTIALS, credentialBinding, deploymentCredentialSchema } from './credential-schema';
12
+ export type { AgentCredentialLocation, DeploymentCredentialBinding, DeploymentCredentialSchema } from './credential-schema';
10
13
  export { createDeploymentManager, DeploymentError } from './deployment';
11
14
  export type { DeploymentManager, DeploymentOptions, DeploymentRequest, DeploymentResult } from './deployment';
12
15
  export { DEFAULT_CONTROL_SOCKET, requestControl, startControlServer } from './control';
@@ -132,6 +135,36 @@ export declare function createSystemdDeploymentSecrets(names: string | undefined
132
135
  has(name: string): boolean;
133
136
  get(name: string): Promise<string>;
134
137
  } | undefined;
138
+ /**
139
+ * Resolve deployment secrets from the project-scoped RAM Vault replica first,
140
+ * then from an explicitly allow-listed systemd credential with the same name.
141
+ * This is shared by platform and tenant deployment managers.
142
+ */
143
+ export declare function createDeploymentSecretResolver(schema: DeploymentCredentialSchema, primary: Pick<SecretCache, 'get'> | undefined, fallback: {
144
+ has(name: string): boolean;
145
+ get(name: string): Promise<string>;
146
+ } | undefined): Pick<SecretCache, 'get'> | undefined;
147
+ export type InitialVaultReplicaStatus = {
148
+ state: 'ready';
149
+ loaded: number;
150
+ failed: readonly string[];
151
+ } | {
152
+ state: 'partial';
153
+ loaded: number;
154
+ failed: readonly string[];
155
+ } | {
156
+ state: 'unavailable';
157
+ loaded: 0;
158
+ failed: readonly [];
159
+ };
160
+ /**
161
+ * Attempt the in-memory Vault replica without turning a locked or restarting
162
+ * Vault into an Agent outage. Deployment reads still try this cache first and
163
+ * may fall back only through the explicit same-name systemd schema. The sync
164
+ * loop remains active and promotes the cache to a complete replica once Vault
165
+ * is available again.
166
+ */
167
+ export declare function initializeVaultReplica(cache: Pick<SecretCache, 'load'>): Promise<InitialVaultReplicaStatus>;
135
168
  /**
136
169
  * Bring the agent up.
137
170
  *
@@ -293,7 +293,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
293
293
  }
294
294
 
295
295
  // src/version.ts
296
- var VERSION = "0.1.39";
296
+ var VERSION = "0.1.40";
297
297
 
298
298
  // src/metal-bootstrap.ts
299
299
  var PROFILE_PATH = "/etc/forgezero/metal.json";
@@ -82,6 +82,8 @@ export interface ApiRuntimeRenderOptions {
82
82
  greenPort: number;
83
83
  collectorUnit: string;
84
84
  credentials: SystemdCredentialSpec[];
85
+ /** Root-owned calibration override, loaded after the ordinary shared environment. */
86
+ capacityEnvironmentFile?: string;
85
87
  }
86
88
  export declare function renderPlatformApiUnits(input: ApiRuntimeRenderOptions): {
87
89
  template: string;
@@ -90,6 +92,8 @@ export declare function renderPlatformApiUnits(input: ApiRuntimeRenderOptions):
90
92
  export declare function renderPlatformNginx(input: {
91
93
  publicPort: number;
92
94
  initialSlotPort: number;
95
+ concurrencyLimit?: number;
96
+ workerDrainSeconds?: number;
93
97
  }): {
94
98
  upstream: string;
95
99
  site: string;
@@ -109,6 +113,7 @@ export interface PlatformActivationConfig {
109
113
  greenPort: number;
110
114
  healthPath: string;
111
115
  keepReleases: number;
116
+ drainDeadlineMs?: number;
112
117
  }
113
118
  /**
114
119
  * Render the fixed privilege boundary used by the credential-free deployment
@@ -218,6 +218,8 @@ function renderPlatformApiUnits(input) {
218
218
  throw new Error("Blue and green ports must differ.");
219
219
  const credentials = input.credentials.map((credential) => `LoadCredentialEncrypted=${credential.name}:${credential.encryptedPath}`).join(`
220
220
  `);
221
+ const capacityEnvironment = input.capacityEnvironmentFile ? `EnvironmentFile=-${input.capacityEnvironmentFile}
222
+ ` : "";
221
223
  const template = `[Unit]
222
224
  Description=ForgeZero (%i slot)
223
225
  After=network-online.target ${input.collectorUnit}
@@ -230,7 +232,7 @@ WorkingDirectory=${input.slotsDirectory}/%i
230
232
  Environment=NODE_ENV=production
231
233
  Environment=FZ_SLOT=%i
232
234
  EnvironmentFile=${input.sharedEnvironmentFile}
233
- ${credentials}
235
+ ${capacityEnvironment}${credentials}
234
236
  ExecStart=/usr/local/bin/bun run ${input.slotsDirectory}/%i/src/index.ts
235
237
  Restart=always
236
238
  RestartSec=2
@@ -266,16 +268,24 @@ Environment=PORT=${input.greenPort}
266
268
  function renderPlatformNginx(input) {
267
269
  boundedInteger("publicPort", input.publicPort, 1024, 65535);
268
270
  boundedInteger("initialSlotPort", input.initialSlotPort, 1024, 65535);
271
+ const concurrencyLimit = boundedInteger("concurrencyLimit", input.concurrencyLimit ?? 256, 1, 1e6);
272
+ boundedInteger("workerDrainSeconds", input.workerDrainSeconds ?? 35, 1, 300);
269
273
  if (input.publicPort === input.initialSlotPort)
270
274
  throw new Error("Edge and slot ports must differ.");
271
275
  return {
272
276
  upstream: `upstream forgezero { server 127.0.0.1:${input.initialSlotPort}; }
273
277
  `,
274
- site: `server {
278
+ site: `limit_conn_zone $server_name zone=forgezero_admission:10m;
279
+ map $http_upgrade $forgezero_connection { default upgrade; '' close; }
280
+ map $limit_conn_status $forgezero_retry_after { default ''; REJECTED 1; REJECTED_DRY_RUN 1; }
281
+ server {
275
282
  listen 127.0.0.1:${input.publicPort};
276
283
  server_name _;
277
- location ^~ /api/ { proxy_pass http://forgezero; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
278
- location ^~ /v1/ { proxy_pass http://forgezero; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
284
+ limit_conn forgezero_admission ${concurrencyLimit};
285
+ limit_conn_status 503;
286
+ add_header Retry-After $forgezero_retry_after always;
287
+ location ^~ /api/ { proxy_pass http://forgezero; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $forgezero_connection; proxy_read_timeout 3600s; }
288
+ location ^~ /v1/ { proxy_pass http://forgezero; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $forgezero_connection; proxy_read_timeout 3600s; }
279
289
  location / { return 404; }
280
290
  }
281
291
  `
@@ -301,6 +311,7 @@ function renderPlatformActivationFiles(input) {
301
311
  if (input.bluePort === input.greenPort)
302
312
  throw new Error("Activation slot ports must differ.");
303
313
  boundedInteger("keepReleases", input.keepReleases, 2, 100);
314
+ const drainDeadlineMs = boundedInteger("drainDeadlineMs", input.drainDeadlineMs ?? 35000, 1000, 300000);
304
315
  if (!/^\/[A-Za-z0-9/_-]{1,128}$/.test(input.healthPath) || input.healthPath.includes("..")) {
305
316
  throw new Error("Activation health path is malformed.");
306
317
  }
@@ -310,7 +321,8 @@ function renderPlatformActivationFiles(input) {
310
321
  `FZ_BLUE_PORT=${input.bluePort}`,
311
322
  `FZ_GREEN_PORT=${input.greenPort}`,
312
323
  `FZ_HEALTH_PATH=${input.healthPath}`,
313
- `FZ_KEEP_RELEASES=${input.keepReleases}`
324
+ `FZ_KEEP_RELEASES=${input.keepReleases}`,
325
+ `FZ_DRAIN_DEADLINE_MS=${drainDeadlineMs}`
314
326
  ].join(`
315
327
  `) + `
316
328
  `;
@@ -332,7 +344,14 @@ if (( ! healthy )); then systemctl stop "forgezero@\${target}.service" || true;
332
344
  upstream=/etc/nginx/conf.d/forgezero-upstream.conf; backup="$(mktemp -p /run forgezero-upstream.XXXXXX)"; [[ -f "$upstream" ]] && cp "$upstream" "$backup" || : >"$backup"
333
345
  printf 'upstream forgezero { server 127.0.0.1:%s; }\\n' "$port" >"$upstream"
334
346
  if ! nginx -t || ! nginx -s reload; then [[ -s "$backup" ]] && cp "$backup" "$upstream" || rm -f "$upstream"; rm -f "$backup"; systemctl stop "forgezero@\${target}.service" || true; [[ -n "$previous_target_link" && -d "$previous_target_link" ]] && ln -sfn "$previous_target_link" "$target_link" || rm -f "$target_link"; nginx -t >/dev/null 2>&1 && nginx -s reload || true; exit 1; fi
335
- rm -f "$backup"; printf '%s\\n' "$target" >"$slot_file"; [[ -n "$previous_slot" && "$previous_slot" != "$target" ]] && systemctl stop "forgezero@\${previous_slot}.service" || true
347
+ rm -f "$backup"; printf '%s\\n' "$target" >"$slot_file"
348
+ # New nginx workers select the new slot after reload. Keep the old slot alive
349
+ # while old workers drain in-flight requests and upgraded connections.
350
+ if [[ -n "$previous_slot" && "$previous_slot" != "$target" ]]; then
351
+ sleep_seconds="$(( (FZ_DRAIN_DEADLINE_MS + 999) / 1000 ))"
352
+ sleep "$sleep_seconds"
353
+ systemctl stop "forgezero@\${previous_slot}.service" || true
354
+ fi
336
355
  mapfile -t old < <(find "$releases" -mindepth 1 -maxdepth 1 -type d -printf '%T@ %p\\n' | sort -rn | tail -n "+$((FZ_KEEP_RELEASES + 1))" | cut -d' ' -f2-)
337
356
  for path in "\${old[@]}"; do [[ "$path" == "$release" ]] || rm -rf -- "$path"; done
338
357
  printf 'promoted %s on %s\\n' "$release" "$target"
@@ -105,7 +105,7 @@ function defaultProjectContext(root = process.cwd()) {
105
105
  "Update a truth source instead of copying architecture or progress into another document.",
106
106
  "Never report a feature as complete without running its declared verification."
107
107
  ],
108
- nonAuthoritative: ["audit/"]
108
+ nonAuthoritative: ["docs/audit/"]
109
109
  };
110
110
  }
111
111
  function renderProjectContext(manifest) {
package/dist/provision.js CHANGED
@@ -93,6 +93,8 @@ var checked = async (run, input, label) => {
93
93
  return result;
94
94
  };
95
95
  async function validateReleaseDirectory(directory, release, run) {
96
+ chmodSync(directory, 493);
97
+ chmodSync(join(directory, "dist"), 493);
96
98
  const manifest = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
97
99
  if (manifest.name !== release.package || manifest.version !== release.version) {
98
100
  throw new Error("agent update manifest does not match the selected release");
@@ -143,19 +145,19 @@ async function stageAgentRelease(releaseInput, options) {
143
145
  if (!timingSafeEqual(actual, expected))
144
146
  throw new Error("agent update integrity mismatch");
145
147
  writeFileSync(archive, bytes, { mode: 384, flag: "wx" });
146
- await checked(run, {
147
- command: "/usr/bin/tar",
148
- args: [
149
- "-xzf",
150
- archive,
151
- "-C",
152
- unpacked,
153
- "--strip-components=1",
154
- "package/package.json",
155
- "package/dist/fz-agent.js",
156
- "package/dist/fz.js"
157
- ]
158
- }, "agent update extraction");
148
+ for (const [member, relative] of [
149
+ ["package/package.json", "package.json"],
150
+ ["package/dist/fz-agent.js", "dist/fz-agent.js"],
151
+ ["package/dist/fz.js", "dist/fz.js"]
152
+ ]) {
153
+ const extracted = await checked(run, {
154
+ command: "/usr/bin/tar",
155
+ args: ["-xOzf", archive, member]
156
+ }, `agent update extraction of ${member}`);
157
+ const destination = join(unpacked, relative);
158
+ mkdirSync(dirname(destination), { recursive: true, mode: 448 });
159
+ writeFileSync(destination, extracted.output, { mode: 384, flag: "wx" });
160
+ }
159
161
  await validateReleaseDirectory(unpacked, release, run);
160
162
  if (!existsSync(finalDirectory)) {
161
163
  renameSync(unpacked, finalDirectory);
@@ -701,8 +703,8 @@ var UBUNTU_2604_X64 = [
701
703
  },
702
704
  {
703
705
  requirement: { id: "arangodb", version: "3.11.14" },
704
- check: `arangod --version 2>/dev/null | head -1 | grep -q '3.11.14'`,
705
- install: `tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT; ` + `curl -fsSL 'https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb' -o "$tmp/arangodb.deb"; ` + `echo "${ARANGO_SHA256} $tmp/arangodb.deb" | sha256sum -c -; ` + `DEBIAN_FRONTEND=noninteractive dpkg -i "$tmp/arangodb.deb" >/dev/null 2>&1 || ` + `DEBIAN_FRONTEND=noninteractive apt-get -y -f install`
706
+ check: `arangod --version 2>/dev/null | head -1 | grep -q '3.11.14' && ` + `! systemctl is-active --quiet arangodb3.service && ` + `! systemctl is-enabled --quiet arangodb3.service`,
707
+ install: `tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT; ` + `curl -fsSL 'https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb' -o "$tmp/arangodb.deb"; ` + `echo "${ARANGO_SHA256} $tmp/arangodb.deb" | sha256sum -c -; ` + `DEBIAN_FRONTEND=noninteractive dpkg -i "$tmp/arangodb.deb" >/dev/null 2>&1 || ` + `DEBIAN_FRONTEND=noninteractive apt-get -y -f install; ` + `systemctl disable --now arangodb3.service`
706
708
  },
707
709
  {
708
710
  requirement: { id: "cloudflared", version: "2026.7.3" },
@@ -891,7 +893,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
891
893
  }
892
894
 
893
895
  // src/version.ts
894
- var VERSION3 = "0.1.39";
896
+ var VERSION3 = "0.1.40";
895
897
 
896
898
  // src/egress-policy.ts
897
899
  import { realpathSync } from "node:fs";
@@ -28,8 +28,8 @@ var UBUNTU_2604_X64 = [
28
28
  },
29
29
  {
30
30
  requirement: { id: "arangodb", version: "3.11.14" },
31
- check: `arangod --version 2>/dev/null | head -1 | grep -q '3.11.14'`,
32
- install: `tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT; ` + `curl -fsSL 'https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb' -o "$tmp/arangodb.deb"; ` + `echo "${ARANGO_SHA256} $tmp/arangodb.deb" | sha256sum -c -; ` + `DEBIAN_FRONTEND=noninteractive dpkg -i "$tmp/arangodb.deb" >/dev/null 2>&1 || ` + `DEBIAN_FRONTEND=noninteractive apt-get -y -f install`
31
+ check: `arangod --version 2>/dev/null | head -1 | grep -q '3.11.14' && ` + `! systemctl is-active --quiet arangodb3.service && ` + `! systemctl is-enabled --quiet arangodb3.service`,
32
+ install: `tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT; ` + `curl -fsSL 'https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb' -o "$tmp/arangodb.deb"; ` + `echo "${ARANGO_SHA256} $tmp/arangodb.deb" | sha256sum -c -; ` + `DEBIAN_FRONTEND=noninteractive dpkg -i "$tmp/arangodb.deb" >/dev/null 2>&1 || ` + `DEBIAN_FRONTEND=noninteractive apt-get -y -f install; ` + `systemctl disable --now arangodb3.service`
33
33
  },
34
34
  {
35
35
  requirement: { id: "cloudflared", version: "2026.7.3" },
package/dist/software.js CHANGED
@@ -28,8 +28,8 @@ var UBUNTU_2604_X64 = [
28
28
  },
29
29
  {
30
30
  requirement: { id: "arangodb", version: "3.11.14" },
31
- check: `arangod --version 2>/dev/null | head -1 | grep -q '3.11.14'`,
32
- install: `tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT; ` + `curl -fsSL 'https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb' -o "$tmp/arangodb.deb"; ` + `echo "${ARANGO_SHA256} $tmp/arangodb.deb" | sha256sum -c -; ` + `DEBIAN_FRONTEND=noninteractive dpkg -i "$tmp/arangodb.deb" >/dev/null 2>&1 || ` + `DEBIAN_FRONTEND=noninteractive apt-get -y -f install`
31
+ check: `arangod --version 2>/dev/null | head -1 | grep -q '3.11.14' && ` + `! systemctl is-active --quiet arangodb3.service && ` + `! systemctl is-enabled --quiet arangodb3.service`,
32
+ install: `tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT; ` + `curl -fsSL 'https://download.arangodb.com/arangodb311/DEBIAN/amd64/arangodb3_3.11.14-1_amd64.deb' -o "$tmp/arangodb.deb"; ` + `echo "${ARANGO_SHA256} $tmp/arangodb.deb" | sha256sum -c -; ` + `DEBIAN_FRONTEND=noninteractive dpkg -i "$tmp/arangodb.deb" >/dev/null 2>&1 || ` + `DEBIAN_FRONTEND=noninteractive apt-get -y -f install; ` + `systemctl disable --now arangodb3.service`
33
33
  },
34
34
  {
35
35
  requirement: { id: "cloudflared", version: "2026.7.3" },
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.39";
2
+ export declare const VERSION = "0.1.40";
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@forgezero/agent",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",
7
7
  "prebuild": "rm -rf dist",
8
- "build": "bun build src/index.ts --outfile dist/fz-agent.js --target bun --format esm && bun build src/cli/index.ts --outfile dist/fz.js --target bun --format esm && bun build src/compute.ts src/provision.ts src/subscribe.ts src/pipeline.ts src/definition.ts src/deploy-file.ts src/ssh-server.ts src/ssh-listen.ts src/provisioning-pull.ts src/migration-pull.ts src/guest-enrolment.ts src/node-vault.ts src/metal-provision.ts src/metal-helper-socket.ts src/lifecycle-helper.ts src/deployment-runner.ts src/agent-update.ts src/agent-update-helper.ts src/agent-heartbeat.ts src/software.ts src/software-helper.ts src/ubuntu.ts src/capacity-calibration.ts src/platform-bootstrap-runtime.ts --root src --outdir dist --target browser --format esm --packages external && bun build src/project-context.ts src/bootstrap.ts src/metal-bootstrap.ts src/cloudflare-bootstrap.ts src/cloudflare-edge.ts --root src --outdir dist --target bun --format esm --packages external && tsc --emitDeclarationOnly --declaration --noEmit false --outDir dist",
8
+ "build": "bun build src/index.ts --outfile dist/fz-agent.js --target bun --format esm && bun build src/cli/index.ts --outfile dist/fz.js --target bun --format esm && bun build src/compute.ts src/provision.ts src/subscribe.ts src/pipeline.ts src/definition.ts src/deploy-file.ts src/ssh-server.ts src/ssh-listen.ts src/provisioning-pull.ts src/migration-pull.ts src/guest-enrolment.ts src/node-vault.ts src/credential-schema.ts src/metal-provision.ts src/metal-helper-socket.ts src/lifecycle-helper.ts src/deployment-runner.ts src/agent-update.ts src/agent-update-helper.ts src/agent-heartbeat.ts src/software.ts src/software-helper.ts src/ubuntu.ts src/capacity-calibration.ts src/platform-bootstrap-runtime.ts --root src --outdir dist --target browser --format esm --packages external && bun build src/project-context.ts src/bootstrap.ts src/metal-bootstrap.ts src/cloudflare-bootstrap.ts src/cloudflare-edge.ts --root src --outdir dist --target bun --format esm --packages external && tsc --emitDeclarationOnly --declaration --noEmit false --outDir dist",
9
9
  "prepublishOnly": "bun run check && bun run build"
10
10
  },
11
11
  "devDependencies": {
12
12
  "typescript": "^5.6.0",
13
13
  "@types/bun": "latest",
14
14
  "@types/node": "^22.0.0",
15
- "@forgezero/access": "0.1.1",
15
+ "@forgezero/access": "0.1.2",
16
16
  "@noble/curves": "^2.2.0",
17
17
  "@noble/post-quantum": "^0.6.1"
18
18
  },
19
19
  "dependencies": {
20
- "@forgezero/runtime": "0.1.5",
20
+ "@forgezero/runtime": "0.1.6",
21
21
  "@forgezero/vault": "0.1.8",
22
22
  "@noble/curves": "2.2.0",
23
23
  "@noble/hashes": "2.2.0",
@@ -150,6 +150,10 @@
150
150
  "types": "./dist/bootstrap.d.ts",
151
151
  "default": "./dist/bootstrap.js"
152
152
  },
153
+ "./credential-schema": {
154
+ "types": "./dist/credential-schema.d.ts",
155
+ "default": "./dist/credential-schema.js"
156
+ },
153
157
  "./platform-bootstrap-runtime": {
154
158
  "types": "./dist/platform-bootstrap-runtime.d.ts",
155
159
  "default": "./dist/platform-bootstrap-runtime.js"
@@ -1,35 +0,0 @@
1
- import { type AgentIdentity } from '@forgezero/runtime/ssh-agent';
2
- /**
3
- * Choosing an SSH key for custody, safely.
4
- *
5
- * A real agent is not a clean room. It commonly holds forwarded keys whose
6
- * upstream connection is gone, and confirm-on-use keys that wait for a human at
7
- * a terminal nobody is sitting at. Neither fails — both **hang**, which during a
8
- * genesis ceremony looks exactly like the platform being broken.
9
- *
10
- * So every key is probed under a timeout before it is offered, and the one
11
- * actually chosen is proved deterministic before it is trusted with a share. An
12
- * agent that signs differently twice would seal a share that can never be
13
- * reopened, and that failure would surface only during recovery.
14
- */
15
- export declare const PROBE_TIMEOUT_MS = 2000;
16
- export interface UsableIdentity extends AgentIdentity {
17
- /** Milliseconds the agent took to sign. Slow keys are usually forwarded. */
18
- responseMs: number;
19
- }
20
- /**
21
- * Every Ed25519 key in the agent that actually responds.
22
- *
23
- * Returns an empty array rather than throwing when the agent holds nothing
24
- * usable — the caller has a better error to give than this function does.
25
- */
26
- export declare function usableIdentities(socketPath?: string): Promise<UsableIdentity[]>;
27
- /**
28
- * Derive the custody key for a chosen identity, having proved it reproduces.
29
- *
30
- * `assertDeterministic` signs twice and compares. It costs one extra signature
31
- * and removes the only failure mode that is invisible until recovery.
32
- */
33
- export declare function custodyKeyFor(identity: AgentIdentity, socketPath?: string): Promise<Uint8Array>;
34
- /** Match a key by fingerprint prefix, comment, or 1-based index. */
35
- export declare function selectIdentity(identities: UsableIdentity[], selector: string): UsableIdentity | null;