@forgezero/agent 0.1.41 → 0.1.42

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.
Files changed (64) hide show
  1. package/README.md +299 -86
  2. package/dist/agent-heartbeat.js +6 -3
  3. package/dist/agent-update-helper.js +5 -2
  4. package/dist/agent-update.js +5 -2
  5. package/dist/bootstrap.d.ts +17 -8
  6. package/dist/bootstrap.js +1504 -512
  7. package/dist/cli/agent-install.d.ts +6 -5
  8. package/dist/cli/cloudflare-bootstrap.d.ts +12 -1
  9. package/dist/cli/maintenance.d.ts +23 -0
  10. package/dist/cli/run.d.ts +3 -1
  11. package/dist/cli/session-store.d.ts +5 -0
  12. package/dist/cloudflare-bootstrap.d.ts +73 -35
  13. package/dist/cloudflare-bootstrap.js +587 -90
  14. package/dist/cloudflare-edge.d.ts +64 -12
  15. package/dist/cloudflare-edge.js +103 -8
  16. package/dist/community-rehearsal-host.d.ts +51 -0
  17. package/dist/community-rehearsal-host.js +272 -0
  18. package/dist/credential-schema.d.ts +54 -0
  19. package/dist/credential-schema.js +47 -0
  20. package/dist/definition.d.ts +31 -5
  21. package/dist/definition.js +271 -44
  22. package/dist/deploy-file.js +294 -68
  23. package/dist/deployment-runner.js +18 -5
  24. package/dist/deployment.d.ts +13 -1
  25. package/dist/fz-agent.js +3932 -582
  26. package/dist/fz-git-ssh.js +122 -0
  27. package/dist/fz.js +3634 -1266
  28. package/dist/git-ssh.d.ts +5 -0
  29. package/dist/guest-enrolment.d.ts +2 -0
  30. package/dist/guest-enrolment.js +1 -0
  31. package/dist/host-maintenance.d.ts +39 -0
  32. package/dist/host-maintenance.js +135 -0
  33. package/dist/index.d.ts +4 -2
  34. package/dist/mesh-connector.d.ts +16 -0
  35. package/dist/mesh-connector.js +46 -0
  36. package/dist/metal-bootstrap.js +145 -7
  37. package/dist/metal-helper-socket.js +61 -31
  38. package/dist/metal-provision.d.ts +2 -2
  39. package/dist/metal-provision.js +62 -32
  40. package/dist/operator-bootstrap.d.ts +90 -0
  41. package/dist/operator-bootstrap.js +5704 -0
  42. package/dist/otel-collector.d.ts +18 -0
  43. package/dist/pipeline.d.ts +3 -2
  44. package/dist/pipeline.js +1 -1
  45. package/dist/platform-bootstrap-runtime.d.ts +39 -21
  46. package/dist/platform-bootstrap-runtime.js +182 -59
  47. package/dist/platform-fleet-verification.d.ts +19 -0
  48. package/dist/platform-fleet-verification.js +3873 -0
  49. package/dist/platform-genesis-config.d.ts +7 -0
  50. package/dist/platform-genesis.d.ts +17 -0
  51. package/dist/provision.d.ts +76 -3
  52. package/dist/provision.js +1061 -229
  53. package/dist/recovery-host.d.ts +7 -0
  54. package/dist/recovery-host.js +124 -0
  55. package/dist/service-supervisor.d.ts +42 -0
  56. package/dist/software-helper.d.ts +4 -0
  57. package/dist/software-helper.js +865 -63
  58. package/dist/software.d.ts +14 -3
  59. package/dist/software.js +163 -37
  60. package/dist/ssh-bootstrap.d.ts +97 -0
  61. package/dist/supervised-app.d.ts +2 -0
  62. package/dist/version.d.ts +1 -1
  63. package/package.json +175 -164
  64. package/schema/{deploy-v2.json → deploy-v3.json} +53 -6
@@ -339,45 +339,70 @@ var yamlFile = (path, content, permissions) => ` - path: ${JSON.stringify(path)
339
339
  encoding: b64
340
340
  content: ${base64(content)}
341
341
  `;
342
- function guestBootstrapScript(profile, attested = Boolean(profile.confidential), hasEnrolment = true, nodeLabel = "compute") {
342
+ function guestBootstrapOperations(profile, attested = Boolean(profile.confidential), hasEnrolment = true, nodeLabel = "compute") {
343
343
  validateMetalProfile(profile);
344
344
  const agentBun = "/usr/local/lib/forgezero/bun";
345
- const telemetryEndpoint = `'${profile.agentTelemetryEndpoint.replace(/'/g, `'\\''`)}'`;
346
- const attestationSetup = attested ? `# The report device is not part of the encryption path, so a guest can appear
347
- # healthy and encrypted while attestation is silently impossible. Install and
348
- # load the driver shipped by the one pinned image before the agent starts. Do
349
- # not ask an evolving apt repository for an exact old kernel package: the image
350
- # can keep booting after Canonical removes that package from the mirror.
351
- if [[ ! -c /dev/sev-guest ]]; then modprobe sev-guest; fi
352
- test -c /dev/sev-guest
353
- printf 'sev-guest
354
- ' >/etc/modules-load.d/sev-guest.conf
355
- ` : "";
356
- return `#!/usr/bin/env bash
357
- set -Eeuo pipefail
358
- ${attestationSetup}if [[ ! -x /usr/local/bin/bun ]]; then
359
- curl -fsSL https://bun.sh/install -o /run/fz-bun-install
360
- printf '%s %s
361
- ' '${profile.bunInstallerSha256}' /run/fz-bun-install | sha256sum -c -
362
- HOME=/root BUN_INSTALL=${agentBun} BUN_VERSION=${profile.bunVersion} bash /run/fz-bun-install
363
- install -m 0755 ${agentBun}/bin/bun /usr/local/bin/bun
364
- rm -f /run/fz-bun-install
365
- fi
366
- if [[ ! -x /usr/local/lib/forgezero/agent/fz-agent ]] || [[ "$(/usr/local/lib/forgezero/agent/fz-agent --version 2>/dev/null || true)" != "${profile.agentVersion}" ]]; then
367
- env BUN_INSTALL=${agentBun} /usr/local/bin/bun add -g --no-cache --force @forgezero/agent@${profile.agentVersion}
368
- fi
369
- env FZ_API=${profile.apiUrl} OTEL_EXPORTER_OTLP_ENDPOINT=${telemetryEndpoint} FZ_AGENT_BIN=/usr/local/lib/forgezero/agent/fz-agent FZ_AGENT_USER=forgezero-agent FZ_SOCKET_PATH=/run/forgezero/vault.sock FZ_SEED_CREDENTIAL_PATH=/etc/forgezero/creds/agent-seed.cred FZ_GIT_CREDENTIAL_PATH=/etc/forgezero/creds/git-deploy-key.cred FZ_GIT_PUBLIC_KEY_PATH=/etc/forgezero/git/deploy.pub FZ_DEPLOY_ROOT=/opt/forgezero FZ_DEPLOY_PULL=${hasEnrolment ? "true" : "false"} FZ_AGENT_EGRESS_ENFORCE=${hasEnrolment ? "true" : "false"} FZ_NODE_LABEL=${nodeLabel} ${agentBun}/bin/fz agent install --apply${hasEnrolment ? " --enrol" : ""}
370
- `;
345
+ return [
346
+ ...attested ? [
347
+ ["/usr/sbin/modprobe", "sev-guest"],
348
+ ["/usr/bin/test", "-c", "/dev/sev-guest"]
349
+ ] : [],
350
+ [
351
+ "/usr/bin/curl",
352
+ "--fail",
353
+ "--silent",
354
+ "--show-error",
355
+ "--location",
356
+ "https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip",
357
+ "--output",
358
+ "/run/forgezero-bun.zip"
359
+ ],
360
+ ["/usr/bin/sha256sum", "--check", "/etc/forgezero/bun.sha256"],
361
+ ["/usr/bin/unzip", "-q", "/run/forgezero-bun.zip", "-d", "/run/forgezero-bun"],
362
+ ["/usr/bin/install", "-d", "-m", "0755", "/usr/local/lib/forgezero/runtime", agentBun],
363
+ ["/usr/bin/install", "-m", "0755", "/run/forgezero-bun/bun-linux-x64/bun", "/usr/local/lib/forgezero/runtime/bun"],
364
+ ["/usr/bin/ln", "-sfn", "/usr/local/lib/forgezero/runtime/bun", "/usr/local/bin/bun"],
365
+ [
366
+ "/usr/bin/env",
367
+ `BUN_INSTALL=${agentBun}`,
368
+ "/usr/local/bin/bun",
369
+ "add",
370
+ "-g",
371
+ "--no-cache",
372
+ "--force",
373
+ `@forgezero/agent@${profile.agentVersion}`
374
+ ],
375
+ [
376
+ "/usr/bin/env",
377
+ `FZ_API=${profile.apiUrl}`,
378
+ `OTEL_EXPORTER_OTLP_ENDPOINT=${profile.agentTelemetryEndpoint}`,
379
+ "FZ_AGENT_BIN=/usr/local/lib/forgezero/agent/fz-agent",
380
+ "FZ_AGENT_USER=forgezero-agent",
381
+ "FZ_SOCKET_PATH=/run/forgezero/vault.sock",
382
+ "FZ_SEED_CREDENTIAL_PATH=/etc/forgezero/creds/agent-seed.cred",
383
+ "FZ_GIT_CREDENTIAL_PATH=/etc/forgezero/creds/git-deploy-key.cred",
384
+ "FZ_GIT_PUBLIC_KEY_PATH=/etc/forgezero/git/deploy.pub",
385
+ "FZ_DEPLOY_ROOT=/opt/forgezero",
386
+ `FZ_DEPLOY_PULL=${hasEnrolment}`,
387
+ `FZ_AGENT_EGRESS_ENFORCE=${hasEnrolment}`,
388
+ `FZ_NODE_LABEL=${nodeLabel}`,
389
+ `${agentBun}/bin/fz`,
390
+ "agent",
391
+ "install",
392
+ "--apply",
393
+ ...hasEnrolment ? ["--enrol"] : []
394
+ ]
395
+ ];
371
396
  }
372
397
  function cloudInit(profile, claim, manifest) {
373
398
  const enrolled = Boolean(claim.enrolment);
374
- const bootstrap = guestBootstrapScript(profile, claim.spec.confidential, enrolled, manifest.name);
399
+ const bootstrap = guestBootstrapOperations(profile, claim.spec.confidential, enrolled, manifest.name);
375
400
  const access = claim.access;
376
401
  const users = access ? `users:
377
402
  - default
378
403
  - name: ${access.sshUser}
379
404
  groups: [sudo]
380
- shell: /bin/bash
405
+ shell: /bin/dash
381
406
  sudo: ALL=(ALL) NOPASSWD:ALL
382
407
  ssh_authorized_keys:
383
408
  ${access.sshPublicKeys.map((key) => ` - ${JSON.stringify(key)}`).join(`
@@ -385,6 +410,10 @@ ${access.sshPublicKeys.map((key) => ` - ${JSON.stringify(key)}`).join(`
385
410
  ` : "";
386
411
  const enrolmentFiles = enrolled ? yamlFile("/run/forgezero-enrol-token", `${claim.enrolment.token}
387
412
  `, "0600") : "";
413
+ const attestationFile = claim.spec.confidential ? yamlFile("/etc/modules-load.d/sev-guest.conf", `sev-guest
414
+ `, "0644") : "";
415
+ const bunChecksum = yamlFile("/etc/forgezero/bun.sha256", `${profile.bunReleaseSha256} /run/forgezero-bun.zip
416
+ `, "0600");
388
417
  return {
389
418
  userData: `#cloud-config
390
419
  package_update: true
@@ -393,8 +422,9 @@ ${users}disable_root: true
393
422
  # every dynamically claimed repository must be cloneable on a clean image.
394
423
  packages: [curl, ca-certificates, openssl, openssh-client, git, unzip${claim.spec.confidential ? ", python3" : ""}]
395
424
  write_files:
396
- ${enrolmentFiles}${yamlFile("/usr/local/sbin/forgezero-guest-bootstrap", bootstrap, "0700")}runcmd:
397
- - [ bash, /usr/local/sbin/forgezero-guest-bootstrap ]
425
+ ${enrolmentFiles}${attestationFile}${bunChecksum}runcmd:
426
+ ${bootstrap.map((argv) => ` - ${JSON.stringify(argv)}`).join(`
427
+ `)}
398
428
  `,
399
429
  metaData: `instance-id: ${manifest.name}-${claim.attempt}
400
430
  local-hostname: ${manifest.name}
@@ -629,7 +659,7 @@ export {
629
659
  provisionMetalGuest,
630
660
  macForAddress,
631
661
  guestNameFor,
632
- guestBootstrapScript,
662
+ guestBootstrapOperations,
633
663
  cloudInit,
634
664
  allocateCpuPool,
635
665
  allocateAddress,
@@ -0,0 +1,90 @@
1
+ import { type PlatformGenesisEnvironment } from './platform-genesis';
2
+ export interface OperatorSshHop {
3
+ address: string;
4
+ port: number;
5
+ user: string;
6
+ hostKey: string;
7
+ hostKeySha256: string;
8
+ }
9
+ export interface OperatorPlatformBootstrapRequest {
10
+ kind: 'platform-remote';
11
+ target: OperatorSshHop & {
12
+ identityPublicKeyFile: string;
13
+ agentSocket: string;
14
+ jump?: OperatorSshHop;
15
+ };
16
+ platformConfigFile: string;
17
+ }
18
+ export interface OperatorMetalBootstrapRequest {
19
+ kind: 'metal-remote';
20
+ target: OperatorPlatformBootstrapRequest['target'];
21
+ metalConfigFile: string;
22
+ genesis: {
23
+ environment: PlatformGenesisEnvironment;
24
+ sshPublicKeyFiles: string[];
25
+ };
26
+ }
27
+ export type OperatorPlatformBootstrapMode = 'prepare' | 'apply' | 'status';
28
+ export type OperatorMetalBootstrapMode = 'apply' | 'genesis' | 'status';
29
+ export interface OperatorPlatformBootstrapPlan {
30
+ kind: 'platform-remote';
31
+ mode: OperatorPlatformBootstrapMode;
32
+ mutation: boolean;
33
+ target: {
34
+ address: string;
35
+ port: number;
36
+ user: string;
37
+ via?: string;
38
+ };
39
+ steps: readonly string[];
40
+ secretInputs: readonly string[];
41
+ }
42
+ export interface OperatorMetalBootstrapPlan {
43
+ kind: 'metal-remote';
44
+ mode: OperatorMetalBootstrapMode;
45
+ mutation: boolean;
46
+ target: {
47
+ address: string;
48
+ port: number;
49
+ user: string;
50
+ };
51
+ steps: readonly string[];
52
+ genesisNodes: readonly string[];
53
+ secretInputs: readonly string[];
54
+ }
55
+ export interface OperatorCommandResult {
56
+ exitCode: number;
57
+ output: string;
58
+ }
59
+ export type OperatorCommand = (argv: readonly string[], options?: {
60
+ stdin?: string;
61
+ secret?: boolean;
62
+ }) => Promise<OperatorCommandResult>;
63
+ export interface OperatorPlatformBootstrapOptions {
64
+ exec?: OperatorCommand;
65
+ fetch?: typeof fetch;
66
+ fzCliPath?: string;
67
+ fzAgentPath?: string;
68
+ fzGitSshPath?: string;
69
+ }
70
+ export declare function readOperatorPlatformBootstrapRequest(path: string): OperatorPlatformBootstrapRequest;
71
+ export declare function readOperatorMetalBootstrapRequest(path: string): OperatorMetalBootstrapRequest;
72
+ export declare function planOperatorMetalBootstrap(request: OperatorMetalBootstrapRequest, mode: OperatorMetalBootstrapMode): OperatorMetalBootstrapPlan;
73
+ export declare function planOperatorPlatformBootstrap(request: OperatorPlatformBootstrapRequest, mode: OperatorPlatformBootstrapMode): OperatorPlatformBootstrapPlan;
74
+ /**
75
+ * Run one reviewed operator action. No private SSH key, credential value or
76
+ * management token is placed in argv; OpenSSH selects the matching private key
77
+ * from the caller's approved agent using only its public half.
78
+ */
79
+ export declare function applyOperatorPlatformBootstrap(request: OperatorPlatformBootstrapRequest, mode: OperatorPlatformBootstrapMode, options?: OperatorPlatformBootstrapOptions): Promise<{
80
+ plan: OperatorPlatformBootstrapPlan;
81
+ output: string;
82
+ }>;
83
+ /**
84
+ * Install/bootstrap one blank metal host or provision its exact three-node
85
+ * platform seed through the same pinned SSH transport used for guest bootstrap.
86
+ */
87
+ export declare function applyOperatorMetalBootstrap(request: OperatorMetalBootstrapRequest, mode: OperatorMetalBootstrapMode, options?: OperatorPlatformBootstrapOptions): Promise<{
88
+ plan: OperatorMetalBootstrapPlan;
89
+ output: string;
90
+ }>;