@forgezero/agent 0.1.34 → 0.1.36

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
@@ -36,6 +36,56 @@ Install the service explicitly:
36
36
  fz agent install --apply # writes a hardened systemd unit
37
37
  ```
38
38
 
39
+ ## Host bootstrap
40
+
41
+ The package is also the host installer. There is no separate shell setup
42
+ script and no second implementation for tenant or platform machines:
43
+
44
+ ```bash
45
+ fz bootstrap platform # interactive plan, no mutation
46
+ sudo fz bootstrap platform --apply # initial DB+API or API-only host
47
+ sudo fz bootstrap tenant --bootstrap-config ./tenant.json --apply
48
+ sudo fz bootstrap metal --bootstrap-config ./metal.json --apply
49
+ sudo fz bootstrap status
50
+ sudo fz bootstrap repair --bootstrap-config ./original.json --apply
51
+ ```
52
+
53
+ Platform bootstrap supports an elastic Community ArangoDB 3.11.14 fleet. The
54
+ first three database-capable computes establish the writable cluster; later
55
+ database joiners and API-only computes use the same command with their typed
56
+ profile and an API-issued one-time enrolment file. Nothing treats three as a
57
+ maximum.
58
+
59
+ After genesis, a tenant can designate an enrolled compute as a bootstrap
60
+ runner. The API creates a project-bound, expiring job containing only public
61
+ SSH coordinates and a pinned Ed25519 host-key fingerprint. The runner claims it
62
+ with its hybrid node identity, reads its SSH key only from a local systemd
63
+ credential, pins one vetted DNS answer, transfers this packaged CLI, and runs
64
+ the same `fz bootstrap tenant` flow on the target. Claim renewal, completion,
65
+ retry and cancellation are fenced in the compute aggregate; the API never
66
+ stores the SSH private key or a plaintext enrolment token.
67
+
68
+ The attended Cloudflare resource phase is part of the same published command,
69
+ but remains separate from the root host install so its management token never
70
+ enters the API or Agent service environment:
71
+
72
+ ```bash
73
+ chmod 600 cloudflare-bootstrap.json cloudflare-management.token
74
+ fz bootstrap platform cloudflare --bootstrap-config ./cloudflare-bootstrap.json
75
+ fz bootstrap platform cloudflare --bootstrap-config ./cloudflare-bootstrap.json --apply
76
+ ```
77
+
78
+ The reviewed JSON has `format: 1`, kind
79
+ `forgezero-cloudflare-bootstrap-request`, a `checkpointPath`, the typed
80
+ `coordinates`, and `tokenFiles` containing file paths only. `coordinates.nodes`
81
+ explicitly lists each node name, origin hostname, loopback service, Tunnel name
82
+ and Access application name. Relative token, checkpoint and Worker project
83
+ paths resolve beside the config file. Plan is offline. Apply creates/reuses one
84
+ shared KV namespace, Worker and Access service token plus one Tunnel, Access
85
+ application, ingress rule and DNS record per node. The command prints only
86
+ resource IDs; connector, Access and requested runtime-token values stay in the
87
+ atomically updated owner-only checkpoint for direct systemd-credential handoff.
88
+
39
89
  ## What changes for an application
40
90
 
41
91
  Nothing.
@@ -744,7 +744,7 @@ async function postSignedNode(options, path, body) {
744
744
  }
745
745
 
746
746
  // src/version.ts
747
- var VERSION3 = "0.1.34";
747
+ var VERSION3 = "0.1.36";
748
748
 
749
749
  // src/agent-heartbeat.ts
750
750
  var unquote = (value) => value.replace(/^['"]|['"]$/g, "");
@@ -0,0 +1,133 @@
1
+ import { type ProvisionPlan } from './cli/agent-install';
2
+ import { type SoftwareCommandResult, type SoftwareRequirement } from './software';
3
+ import { type PlatformSharedEnvironment } from './platform-bootstrap-runtime';
4
+ export declare const PLATFORM_BOOTSTRAP_PROFILES: readonly ["platform-db-api", "platform-api"];
5
+ export type PlatformBootstrapProfile = (typeof PLATFORM_BOOTSTRAP_PROFILES)[number];
6
+ export type DatabaseBootstrapRole = 'master' | 'joiner' | 'none';
7
+ export type BootstrapEnvironment = 'production' | 'development';
8
+ export interface PlatformBootstrapConfig {
9
+ kind: 'platform';
10
+ environment: BootstrapEnvironment;
11
+ profile: PlatformBootstrapProfile;
12
+ /** API-owned compute reference. Initial seed references are fz-n1..3/dev-fz-n1..3. */
13
+ computeReference: string;
14
+ nodeHostname: string;
15
+ apiUrl: string;
16
+ repository: string;
17
+ branch: string;
18
+ deployRoot?: string;
19
+ telemetryEndpoint: string;
20
+ database: {
21
+ role: DatabaseBootstrapRole;
22
+ /** Cluster mode must remain default. Bootstrap verifies it and never mutates it. */
23
+ serverMode: 'default';
24
+ address?: string;
25
+ master?: string;
26
+ coordinators: string[];
27
+ /** Owner/root-only file containing the shared 64-hex bootstrap code. */
28
+ bootstrapSecretFile: string;
29
+ };
30
+ /** Required for every post-genesis compute; initial n1..n3 derive their one-use capability. */
31
+ platformEnrolTokenFile?: string;
32
+ runtime: {
33
+ environment: PlatformSharedEnvironment;
34
+ serviceUser: string;
35
+ slotsDirectory: string;
36
+ bluePort: number;
37
+ greenPort: number;
38
+ healthPath: string;
39
+ keepReleases: number;
40
+ credentialFiles?: {
41
+ smtpPassword?: string;
42
+ backupS3Secret?: string;
43
+ };
44
+ };
45
+ firewall: {
46
+ enabled: boolean;
47
+ sshPort: number;
48
+ privateCidrs: string[];
49
+ };
50
+ /** Install the connector binary only. Resource creation belongs to attended Cloudflare bootstrap. */
51
+ installCloudflared?: boolean;
52
+ /** Completed attended resource checkpoint; root seals only this node's runtime capabilities. */
53
+ cloudflareHandoff?: {
54
+ checkpointFile: string;
55
+ nodeName: string;
56
+ };
57
+ }
58
+ export interface TenantBootstrapConfig {
59
+ kind: 'tenant';
60
+ apiUrl: string;
61
+ realm: string;
62
+ nodeHostname: string;
63
+ telemetryEndpoint: string;
64
+ enrolTokenFile: string;
65
+ repository?: string;
66
+ branch?: string;
67
+ profile?: string;
68
+ deployRoot?: string;
69
+ software?: SoftwareRequirement[];
70
+ installCloudflared?: boolean;
71
+ /** Local-only runner authority. The API receives only the resulting node scope. */
72
+ bootstrapRunner?: {
73
+ sshPrivateKeyFile: string;
74
+ targetTelemetryEndpoint: string;
75
+ };
76
+ }
77
+ export type BootstrapConfig = PlatformBootstrapConfig | TenantBootstrapConfig;
78
+ export interface BootstrapStep {
79
+ id: string;
80
+ label: string;
81
+ mutation: boolean;
82
+ }
83
+ export interface BootstrapPlan {
84
+ kind: BootstrapConfig['kind'];
85
+ mode: 'install' | 'repair';
86
+ profile: string;
87
+ steps: readonly BootstrapStep[];
88
+ software: readonly SoftwareRequirement[];
89
+ statePath: string;
90
+ }
91
+ export interface BootstrapResult {
92
+ plan: BootstrapPlan;
93
+ applied: boolean;
94
+ status: BootstrapStatus;
95
+ launch?: {
96
+ command: string;
97
+ inviteUrl: string;
98
+ };
99
+ }
100
+ export interface BootstrapStatus {
101
+ initialized: boolean;
102
+ kind?: BootstrapConfig['kind'];
103
+ profile?: string;
104
+ services: Record<string, boolean>;
105
+ problems: string[];
106
+ }
107
+ export interface BootstrapHost {
108
+ uid(): number;
109
+ exists(path: string): boolean;
110
+ read(path: string): string;
111
+ write(path: string, content: string, mode: number): void;
112
+ mkdir(path: string, mode: number): void;
113
+ remove(path: string): void;
114
+ inspect?(path: string): {
115
+ regular: boolean;
116
+ symbolic: boolean;
117
+ uid: number;
118
+ mode: number;
119
+ links: number;
120
+ size: number;
121
+ };
122
+ exec(argv: readonly string[], options?: {
123
+ stdin?: string;
124
+ }): Promise<SoftwareCommandResult>;
125
+ ensureSoftware(requirements: readonly SoftwareRequirement[]): Promise<unknown>;
126
+ installAgent(config: BootstrapConfig, enrolTokenSourcePath?: string): Promise<ProvisionPlan>;
127
+ }
128
+ export declare function validateBootstrapConfig(value: BootstrapConfig): BootstrapConfig;
129
+ export declare function planBootstrap(input: BootstrapConfig, initialized?: boolean): BootstrapPlan;
130
+ export declare function bootstrapStatus(host?: BootstrapHost): Promise<BootstrapStatus>;
131
+ export declare function applyBootstrap(input: BootstrapConfig, host?: BootstrapHost): Promise<BootstrapResult>;
132
+ export declare function readBootstrapConfig(path: string): BootstrapConfig;
133
+ export declare function localBootstrapHost(): BootstrapHost;