@forgezero/agent 0.1.53 → 0.1.55
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 +118 -9
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.js +110 -69
- package/dist/community-rehearsal-host.js +37 -5
- package/dist/container-supervisor.d.ts +36 -0
- package/dist/definition.js +37 -5
- package/dist/deploy-actions.d.ts +87 -0
- package/dist/deploy-compiler.d.ts +26 -0
- package/dist/deploy-compiler.js +1050 -0
- package/dist/deploy-file.js +44 -12
- package/dist/deploy-plan-runner.d.ts +65 -0
- package/dist/deploy-plan-runner.js +1205 -0
- package/dist/deploy-plan.d.ts +41 -0
- package/dist/deploy-plan.js +925 -0
- package/dist/deploy-providers.d.ts +85 -0
- package/dist/deploy.d.ts +439 -0
- package/dist/deploy.js +169 -0
- package/dist/deployment.d.ts +5 -0
- package/dist/fz-agent.js +2113 -326
- package/dist/fz.js +1444 -313
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.js +167 -126
- package/dist/platform-bootstrap-runtime.js +1 -1
- package/dist/platform-fleet-verification.js +499 -120
- package/dist/provision.js +399 -20
- package/dist/software-helper.d.ts +5 -0
- package/dist/software-helper.js +399 -18
- package/dist/software.d.ts +1 -1
- package/dist/software.js +37 -5
- package/dist/version.d.ts +1 -1
- package/package.json +17 -1
|
@@ -1180,15 +1180,15 @@ async function finalizeCloudflareBootstrapAcceptance(request, fetcher = fetch) {
|
|
|
1180
1180
|
import { createHash, createHmac, randomBytes as randomBytes3 } from "crypto";
|
|
1181
1181
|
import {
|
|
1182
1182
|
chmodSync as chmodSync2,
|
|
1183
|
-
existsSync as
|
|
1183
|
+
existsSync as existsSync4,
|
|
1184
1184
|
lstatSync as lstatSync2,
|
|
1185
|
-
mkdirSync as
|
|
1186
|
-
readFileSync as
|
|
1187
|
-
renameSync as
|
|
1188
|
-
rmSync as
|
|
1189
|
-
writeFileSync as
|
|
1185
|
+
mkdirSync as mkdirSync4,
|
|
1186
|
+
readFileSync as readFileSync4,
|
|
1187
|
+
renameSync as renameSync4,
|
|
1188
|
+
rmSync as rmSync4,
|
|
1189
|
+
writeFileSync as writeFileSync4
|
|
1190
1190
|
} from "fs";
|
|
1191
|
-
import { dirname as
|
|
1191
|
+
import { dirname as dirname5 } from "path";
|
|
1192
1192
|
import { fileURLToPath } from "url";
|
|
1193
1193
|
|
|
1194
1194
|
// src/agent-update-helper.ts
|
|
@@ -1207,11 +1207,19 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
1207
1207
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
1208
1208
|
|
|
1209
1209
|
// src/version.ts
|
|
1210
|
-
var VERSION = "0.1.
|
|
1210
|
+
var VERSION = "0.1.55";
|
|
1211
1211
|
|
|
1212
1212
|
// src/software.ts
|
|
1213
1213
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
1214
1214
|
var BUN_RELEASE_SHA256 = "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f";
|
|
1215
|
+
var DOCKER_DAEMON_CONFIG = `${JSON.stringify({
|
|
1216
|
+
"data-root": "/var/lib/docker",
|
|
1217
|
+
"storage-driver": "overlay2",
|
|
1218
|
+
"live-restore": true,
|
|
1219
|
+
"log-driver": "local",
|
|
1220
|
+
"log-opts": { "max-size": "10m", "max-file": "3" }
|
|
1221
|
+
}, null, 2)}
|
|
1222
|
+
`;
|
|
1215
1223
|
|
|
1216
1224
|
// src/service-supervisor.ts
|
|
1217
1225
|
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
@@ -1273,6 +1281,39 @@ var defaultHost = {
|
|
|
1273
1281
|
now: Date.now
|
|
1274
1282
|
};
|
|
1275
1283
|
|
|
1284
|
+
// src/container-supervisor.ts
|
|
1285
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, realpathSync as realpathSync2, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1286
|
+
import { dirname as dirname3, resolve as resolve3, sep as sep2 } from "path";
|
|
1287
|
+
var defaultHost2 = {
|
|
1288
|
+
realpath: realpathSync2,
|
|
1289
|
+
exists: existsSync2,
|
|
1290
|
+
read: (path) => readFileSync2(path, "utf8"),
|
|
1291
|
+
write(path, content, mode) {
|
|
1292
|
+
mkdirSync2(dirname3(path), { recursive: true, mode: 493 });
|
|
1293
|
+
const next = `${path}.next`;
|
|
1294
|
+
writeFileSync2(next, content, { mode });
|
|
1295
|
+
renameSync2(next, path);
|
|
1296
|
+
},
|
|
1297
|
+
remove: (path) => rmSync2(path, { force: true }),
|
|
1298
|
+
list: (path) => existsSync2(path) ? readdirSync2(path) : [],
|
|
1299
|
+
mkdir: (path, mode) => mkdirSync2(path, { recursive: true, mode }),
|
|
1300
|
+
async exec(argv) {
|
|
1301
|
+
const child = Bun.spawn([...argv], { stdout: "pipe", stderr: "pipe", env: { PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", LANG: "C", LC_ALL: "C" } });
|
|
1302
|
+
const [stdout, stderr, exitCode] = await Promise.all([new Response(child.stdout).text(), new Response(child.stderr).text(), child.exited]);
|
|
1303
|
+
return { exitCode, output: `${stdout}${stderr}` };
|
|
1304
|
+
},
|
|
1305
|
+
async health(port, path, timeoutMs, method, expectedStatus) {
|
|
1306
|
+
try {
|
|
1307
|
+
const response = await fetch(`http://127.0.0.1:${port}${path}`, { method, redirect: "manual", signal: AbortSignal.timeout(timeoutMs) });
|
|
1308
|
+
return expectedStatus.includes(response.status);
|
|
1309
|
+
} catch {
|
|
1310
|
+
return false;
|
|
1311
|
+
}
|
|
1312
|
+
},
|
|
1313
|
+
sleep: (ms) => Bun.sleep(ms),
|
|
1314
|
+
now: Date.now
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1276
1317
|
// src/software-helper.ts
|
|
1277
1318
|
var DEFAULT_SOFTWARE_HELPER_SOCKET = "/run/forgezero-software/helper.sock";
|
|
1278
1319
|
var SOFTWARE_HELPER_GROUP = "forgezero-software";
|
|
@@ -2255,17 +2296,17 @@ import { randomBytes as randomBytes2 } from "crypto";
|
|
|
2255
2296
|
import {
|
|
2256
2297
|
chmodSync,
|
|
2257
2298
|
copyFileSync,
|
|
2258
|
-
existsSync as
|
|
2299
|
+
existsSync as existsSync3,
|
|
2259
2300
|
lstatSync,
|
|
2260
|
-
mkdirSync as
|
|
2261
|
-
readFileSync as
|
|
2262
|
-
realpathSync as
|
|
2263
|
-
renameSync as
|
|
2264
|
-
rmSync as
|
|
2301
|
+
mkdirSync as mkdirSync3,
|
|
2302
|
+
readFileSync as readFileSync3,
|
|
2303
|
+
realpathSync as realpathSync3,
|
|
2304
|
+
renameSync as renameSync3,
|
|
2305
|
+
rmSync as rmSync3,
|
|
2265
2306
|
symlinkSync,
|
|
2266
|
-
writeFileSync as
|
|
2307
|
+
writeFileSync as writeFileSync3
|
|
2267
2308
|
} from "fs";
|
|
2268
|
-
import { dirname as
|
|
2309
|
+
import { dirname as dirname4 } from "path";
|
|
2269
2310
|
async function readCapabilities(run) {
|
|
2270
2311
|
const answers = {};
|
|
2271
2312
|
const checks = Object.entries(CAPABILITY_CHECKS);
|
|
@@ -2324,28 +2365,28 @@ var runProvisionOperation = async (operation) => {
|
|
|
2324
2365
|
}
|
|
2325
2366
|
if (operation.kind === "install-runtime") {
|
|
2326
2367
|
const release = `/opt/forgezero/agent/versions/${operation.version}`;
|
|
2327
|
-
|
|
2328
|
-
|
|
2368
|
+
mkdirSync3(`${release}/dist`, { recursive: true, mode: 493 });
|
|
2369
|
+
mkdirSync3(dirname4(operation.binary), { recursive: true, mode: 493 });
|
|
2329
2370
|
copyFileSync(operation.source, `${release}/dist/fz-agent.js`);
|
|
2330
2371
|
chmodSync(`${release}/dist/fz-agent.js`, 493);
|
|
2331
|
-
const gitSshSource = `${
|
|
2332
|
-
if (!
|
|
2372
|
+
const gitSshSource = `${dirname4(operation.source)}/fz-git-ssh.js`;
|
|
2373
|
+
if (!existsSync3(gitSshSource))
|
|
2333
2374
|
return { stdout: "packaged fz-git-ssh.js is missing", exitCode: 1 };
|
|
2334
2375
|
copyFileSync(gitSshSource, `${release}/dist/fz-git-ssh.js`);
|
|
2335
2376
|
chmodSync(`${release}/dist/fz-git-ssh.js`, 493);
|
|
2336
2377
|
const pending = "/opt/forgezero/agent/current.next";
|
|
2337
|
-
|
|
2378
|
+
rmSync3(pending, { force: true });
|
|
2338
2379
|
symlinkSync(`versions/${operation.version}`, pending);
|
|
2339
|
-
|
|
2340
|
-
|
|
2380
|
+
renameSync3(pending, "/opt/forgezero/agent/current");
|
|
2381
|
+
rmSync3(operation.binary, { force: true });
|
|
2341
2382
|
symlinkSync("/opt/forgezero/agent/current/dist/fz-agent.js", operation.binary);
|
|
2342
2383
|
const gitSshBinary = "/usr/local/lib/forgezero/agent/fz-git-ssh";
|
|
2343
|
-
|
|
2384
|
+
rmSync3(gitSshBinary, { force: true });
|
|
2344
2385
|
symlinkSync("/opt/forgezero/agent/current/dist/fz-git-ssh.js", gitSshBinary);
|
|
2345
2386
|
return { stdout: "", exitCode: 0 };
|
|
2346
2387
|
}
|
|
2347
2388
|
if (operation.kind === "ensure-seed") {
|
|
2348
|
-
if (
|
|
2389
|
+
if (existsSync3(operation.credential) && lstatSync(operation.credential).size > 0)
|
|
2349
2390
|
return { stdout: "", exitCode: 0 };
|
|
2350
2391
|
const seed = randomBytes2(32).toString("base64url");
|
|
2351
2392
|
const result = await fixed(["/usr/bin/systemd-creds", "encrypt", "--name=agent-seed", "-", operation.credential], seed);
|
|
@@ -2357,9 +2398,9 @@ var runProvisionOperation = async (operation) => {
|
|
|
2357
2398
|
const key = "/run/forgezero-git-deploy-key";
|
|
2358
2399
|
const publicKey = `${key}.pub`;
|
|
2359
2400
|
try {
|
|
2360
|
-
if (!
|
|
2361
|
-
|
|
2362
|
-
|
|
2401
|
+
if (!existsSync3(operation.credential) || lstatSync(operation.credential).size < 1) {
|
|
2402
|
+
rmSync3(key, { force: true });
|
|
2403
|
+
rmSync3(publicKey, { force: true });
|
|
2363
2404
|
let result = await fixed(["/usr/bin/ssh-keygen", "-q", "-t", "ed25519", "-N", "", "-C", "forgezero-compute", "-f", key]);
|
|
2364
2405
|
if (result.exitCode !== 0)
|
|
2365
2406
|
return result;
|
|
@@ -2368,8 +2409,8 @@ var runProvisionOperation = async (operation) => {
|
|
|
2368
2409
|
return result;
|
|
2369
2410
|
chmodSync(operation.credential, 256);
|
|
2370
2411
|
}
|
|
2371
|
-
if (!
|
|
2372
|
-
if (!
|
|
2412
|
+
if (!existsSync3(operation.publicKey) || lstatSync(operation.publicKey).size < 1) {
|
|
2413
|
+
if (!existsSync3(key)) {
|
|
2373
2414
|
const decrypted = await fixed(["/usr/bin/systemd-creds", "decrypt", "--name=git-deploy-key", operation.credential, key]);
|
|
2374
2415
|
if (decrypted.exitCode !== 0)
|
|
2375
2416
|
return decrypted;
|
|
@@ -2377,25 +2418,25 @@ var runProvisionOperation = async (operation) => {
|
|
|
2377
2418
|
const derived = await fixed(["/usr/bin/ssh-keygen", "-y", "-f", key]);
|
|
2378
2419
|
if (derived.exitCode !== 0)
|
|
2379
2420
|
return derived;
|
|
2380
|
-
|
|
2421
|
+
writeFileSync3(operation.publicKey, `${derived.stdout.trim()} forgezero-compute
|
|
2381
2422
|
`, { mode: 292 });
|
|
2382
2423
|
}
|
|
2383
2424
|
return { stdout: "", exitCode: 0 };
|
|
2384
2425
|
} finally {
|
|
2385
|
-
|
|
2386
|
-
|
|
2426
|
+
rmSync3(key, { force: true });
|
|
2427
|
+
rmSync3(publicKey, { force: true });
|
|
2387
2428
|
}
|
|
2388
2429
|
}
|
|
2389
2430
|
if (operation.kind === "ensure-bootstrap-ssh-identity") {
|
|
2390
2431
|
const key = "/run/forgezero-bootstrap-ssh-key";
|
|
2391
2432
|
const generatedPublicKey = `${key}.pub`;
|
|
2392
2433
|
try {
|
|
2393
|
-
if (!
|
|
2394
|
-
|
|
2395
|
-
|
|
2434
|
+
if (!existsSync3(operation.credential) || lstatSync(operation.credential).size < 1) {
|
|
2435
|
+
rmSync3(key, { force: true });
|
|
2436
|
+
rmSync3(generatedPublicKey, { force: true });
|
|
2396
2437
|
let result;
|
|
2397
2438
|
if (operation.source) {
|
|
2398
|
-
const source =
|
|
2439
|
+
const source = existsSync3(operation.source) ? lstatSync(operation.source) : undefined;
|
|
2399
2440
|
if (!source?.isFile() || source.isSymbolicLink() || source.uid !== 0 || source.nlink !== 1 || (source.mode & 63) !== 0 || source.size < 32 || source.size > 16 * 1024) {
|
|
2400
2441
|
return { stdout: "bootstrap SSH private-key source is missing or unsafe", exitCode: 1 };
|
|
2401
2442
|
}
|
|
@@ -2415,8 +2456,8 @@ var runProvisionOperation = async (operation) => {
|
|
|
2415
2456
|
return result;
|
|
2416
2457
|
chmodSync(operation.credential, 256);
|
|
2417
2458
|
}
|
|
2418
|
-
if (!
|
|
2419
|
-
if (!
|
|
2459
|
+
if (!existsSync3(operation.publicKey) || lstatSync(operation.publicKey).size < 1) {
|
|
2460
|
+
if (!existsSync3(key)) {
|
|
2420
2461
|
const decrypted = await fixed(["/usr/bin/systemd-creds", "decrypt", "--name=bootstrap-ssh-key", operation.credential, key]);
|
|
2421
2462
|
if (decrypted.exitCode !== 0)
|
|
2422
2463
|
return decrypted;
|
|
@@ -2426,28 +2467,28 @@ var runProvisionOperation = async (operation) => {
|
|
|
2426
2467
|
if (derived.exitCode !== 0 || !/^ssh-ed25519 [A-Za-z0-9+/]+={0,3}\s*$/.test(derived.stdout)) {
|
|
2427
2468
|
return { stdout: "bootstrap SSH public key derivation failed", exitCode: 1 };
|
|
2428
2469
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2470
|
+
mkdirSync3(dirname4(operation.publicKey), { recursive: true, mode: 493 });
|
|
2471
|
+
writeFileSync3(operation.publicKey, `${derived.stdout.trim()} forgezero-bootstrap-runner
|
|
2431
2472
|
`, { mode: 292 });
|
|
2432
2473
|
chmodSync(operation.publicKey, 292);
|
|
2433
2474
|
}
|
|
2434
2475
|
if (operation.source)
|
|
2435
|
-
|
|
2476
|
+
rmSync3(operation.source, { force: true });
|
|
2436
2477
|
return { stdout: "", exitCode: 0 };
|
|
2437
2478
|
} finally {
|
|
2438
|
-
|
|
2439
|
-
|
|
2479
|
+
rmSync3(key, { force: true });
|
|
2480
|
+
rmSync3(generatedPublicKey, { force: true });
|
|
2440
2481
|
}
|
|
2441
2482
|
}
|
|
2442
2483
|
if (operation.kind === "ensure-enrolment") {
|
|
2443
|
-
if (
|
|
2484
|
+
if (existsSync3(operation.state) && lstatSync(operation.state).size > 0 || existsSync3(operation.credential) && lstatSync(operation.credential).size > 0)
|
|
2444
2485
|
return { stdout: "", exitCode: 0 };
|
|
2445
|
-
if (!
|
|
2486
|
+
if (!existsSync3(operation.source))
|
|
2446
2487
|
return { stdout: "enrolment source is missing", exitCode: 1 };
|
|
2447
2488
|
const result = await fixed(["/usr/bin/systemd-creds", "encrypt", "--name=enrol-token", operation.source, operation.credential]);
|
|
2448
2489
|
if (result.exitCode === 0) {
|
|
2449
2490
|
chmodSync(operation.credential, 256);
|
|
2450
|
-
|
|
2491
|
+
rmSync3(operation.source, { force: true });
|
|
2451
2492
|
}
|
|
2452
2493
|
return result;
|
|
2453
2494
|
}
|
|
@@ -2462,7 +2503,7 @@ var runProvisionOperation = async (operation) => {
|
|
|
2462
2503
|
return { stdout: `socket did not become ready: ${operation.path}`, exitCode: 1 };
|
|
2463
2504
|
}
|
|
2464
2505
|
if (operation.kind === "verify-file")
|
|
2465
|
-
return
|
|
2506
|
+
return existsSync3(operation.path) && lstatSync(operation.path).size > 0 ? { stdout: "", exitCode: 0 } : { stdout: "required file is empty", exitCode: 1 };
|
|
2466
2507
|
if (operation.kind === "verify-egress") {
|
|
2467
2508
|
const active = await fixed(["/usr/bin/systemctl", "is-active", "forgezero-agent-egress.service"]);
|
|
2468
2509
|
if (active.exitCode !== 0)
|
|
@@ -2478,31 +2519,31 @@ var runProvisionOperation = async (operation) => {
|
|
|
2478
2519
|
if (operation.kind === "verify-resolved-stub") {
|
|
2479
2520
|
try {
|
|
2480
2521
|
const expected = "/run/systemd/resolve/stub-resolv.conf";
|
|
2481
|
-
return
|
|
2522
|
+
return realpathSync3("/etc/resolv.conf") === expected && realpathSync3(expected) === expected ? { stdout: expected, exitCode: 0 } : { stdout: "resolver stub mismatch", exitCode: 1 };
|
|
2482
2523
|
} catch {
|
|
2483
2524
|
return { stdout: "resolver stub missing", exitCode: 1 };
|
|
2484
2525
|
}
|
|
2485
2526
|
}
|
|
2486
2527
|
if (operation.kind === "install-warp") {
|
|
2487
|
-
const os =
|
|
2528
|
+
const os = readFileSync3("/etc/os-release", "utf8");
|
|
2488
2529
|
if (!/^ID=ubuntu$/m.test(os) || !/^VERSION_ID="?26\.04"?$/m.test(os))
|
|
2489
2530
|
return { stdout: "unsupported WARP host OS", exitCode: 1 };
|
|
2490
2531
|
const response = await fetch("https://pkg.cloudflareclient.com/pubkey.gpg", { signal: AbortSignal.timeout(30000) });
|
|
2491
2532
|
if (!response.ok)
|
|
2492
2533
|
return { stdout: `WARP key HTTP ${response.status}`, exitCode: 1 };
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2534
|
+
mkdirSync3("/usr/share/keyrings", { recursive: true, mode: 493 });
|
|
2535
|
+
mkdirSync3("/etc/apt/sources.list.d", { recursive: true, mode: 493 });
|
|
2536
|
+
mkdirSync3("/etc/systemd/system/warp-svc.service.d", { recursive: true, mode: 493 });
|
|
2496
2537
|
const key = "/run/cloudflare-warp-key.gpg";
|
|
2497
|
-
|
|
2538
|
+
writeFileSync3(key, new Uint8Array(await response.arrayBuffer()), { mode: 384 });
|
|
2498
2539
|
let result = await fixed(["/usr/bin/gpg", "--batch", "--yes", "--dearmor", "-o", "/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg", key]);
|
|
2499
|
-
|
|
2540
|
+
rmSync3(key, { force: true });
|
|
2500
2541
|
if (result.exitCode !== 0)
|
|
2501
2542
|
return result;
|
|
2502
2543
|
const codename = os.match(/^VERSION_CODENAME=(.+)$/m)?.[1]?.replace(/^"|"$/g, "");
|
|
2503
2544
|
if (!codename)
|
|
2504
2545
|
return { stdout: "Ubuntu codename missing", exitCode: 1 };
|
|
2505
|
-
|
|
2546
|
+
writeFileSync3("/etc/apt/sources.list.d/cloudflare-client.list", `deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ ${codename} main
|
|
2506
2547
|
`, { mode: 420 });
|
|
2507
2548
|
result = await fixed(["/usr/bin/apt-get", "update", "-qq"]);
|
|
2508
2549
|
return result.exitCode === 0 ? fixed(["/usr/bin/apt-get", "install", "-y", "cloudflare-warp"]) : result;
|
|
@@ -4175,7 +4216,7 @@ function readBootstrapConfig(path) {
|
|
|
4175
4216
|
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.uid !== (process.getuid?.() ?? metadata.uid) || metadata.nlink !== 1 || (metadata.mode & 63) !== 0 || metadata.size > 64 * 1024) {
|
|
4176
4217
|
throw new Error("bootstrap config must be an owner-only regular file with one link and at most 64 KiB");
|
|
4177
4218
|
}
|
|
4178
|
-
return validateBootstrapConfig(strictBootstrapDocument(JSON.parse(
|
|
4219
|
+
return validateBootstrapConfig(strictBootstrapDocument(JSON.parse(readFileSync4(path, "utf8"))));
|
|
4179
4220
|
}
|
|
4180
4221
|
function localBootstrapHost() {
|
|
4181
4222
|
const execute = async (argv, options = {}) => {
|
|
@@ -4193,17 +4234,17 @@ function localBootstrapHost() {
|
|
|
4193
4234
|
};
|
|
4194
4235
|
return {
|
|
4195
4236
|
uid: () => process.getuid?.() ?? -1,
|
|
4196
|
-
exists:
|
|
4197
|
-
read: (path) =>
|
|
4237
|
+
exists: existsSync4,
|
|
4238
|
+
read: (path) => readFileSync4(path, "utf8"),
|
|
4198
4239
|
write(path, content, mode) {
|
|
4199
|
-
|
|
4240
|
+
mkdirSync4(dirname5(path), { recursive: true, mode: 493 });
|
|
4200
4241
|
const temporary = `${path}.next.${process.pid}`;
|
|
4201
|
-
|
|
4242
|
+
writeFileSync4(temporary, content, { mode });
|
|
4202
4243
|
chmodSync2(temporary, mode);
|
|
4203
|
-
|
|
4244
|
+
renameSync4(temporary, path);
|
|
4204
4245
|
},
|
|
4205
|
-
mkdir: (path, mode) =>
|
|
4206
|
-
remove: (path) =>
|
|
4246
|
+
mkdir: (path, mode) => mkdirSync4(path, { recursive: true, mode }),
|
|
4247
|
+
remove: (path) => rmSync4(path, { force: true }),
|
|
4207
4248
|
inspect(path) {
|
|
4208
4249
|
const value = lstatSync2(path);
|
|
4209
4250
|
return { regular: value.isFile(), symbolic: value.isSymbolicLink(), uid: value.uid, mode: value.mode, links: value.nlink, size: value.size };
|
|
@@ -4227,7 +4268,7 @@ function localBootstrapHost() {
|
|
|
4227
4268
|
async installAgent(config, enrolTokenSourcePath) {
|
|
4228
4269
|
const capabilities = await readCapabilities(localRunner);
|
|
4229
4270
|
const deployRoot = config.deployRoot ?? "/opt/forgezero";
|
|
4230
|
-
const hasBinding = config.kind === "enrolled-compute" || Boolean(enrolTokenSourcePath) ||
|
|
4271
|
+
const hasBinding = config.kind === "enrolled-compute" || Boolean(enrolTokenSourcePath) || existsSync4("/var/lib/forgezero/enrolment.json");
|
|
4231
4272
|
if (config.kind === "platform") {
|
|
4232
4273
|
const lifecycle = config.database.role === "none" ? {
|
|
4233
4274
|
apiUnits: ["forgezero@blue.service", "forgezero@green.service"],
|
|
@@ -4239,8 +4280,8 @@ function localBootstrapHost() {
|
|
|
4239
4280
|
databaseHealthUrl: `http://${config.database.address}:8529/_api/version`,
|
|
4240
4281
|
databasePorts: [8529]
|
|
4241
4282
|
};
|
|
4242
|
-
|
|
4243
|
-
|
|
4283
|
+
mkdirSync4(dirname5(LIFECYCLE_PROFILE), { recursive: true, mode: 493 });
|
|
4284
|
+
writeFileSync4(LIFECYCLE_PROFILE, `${JSON.stringify(lifecycle, null, 2)}
|
|
4244
4285
|
`, { mode: 256 });
|
|
4245
4286
|
}
|
|
4246
4287
|
const plan = planInstall({
|
|
@@ -4281,8 +4322,8 @@ function localBootstrapHost() {
|
|
|
4281
4322
|
} : {}
|
|
4282
4323
|
});
|
|
4283
4324
|
for (const unit of [{ path: plan.unitPath, unit: plan.unit }, ...plan.auxiliaryUnits]) {
|
|
4284
|
-
|
|
4285
|
-
|
|
4325
|
+
mkdirSync4(dirname5(unit.path), { recursive: true, mode: 493 });
|
|
4326
|
+
writeFileSync4(unit.path, unit.unit, { mode: 420 });
|
|
4286
4327
|
}
|
|
4287
4328
|
await applyPlan(plan, localRunner);
|
|
4288
4329
|
return plan;
|
|
@@ -4296,26 +4337,26 @@ import {
|
|
|
4296
4337
|
chmodSync as chmodSync3,
|
|
4297
4338
|
chownSync,
|
|
4298
4339
|
copyFileSync as copyFileSync2,
|
|
4299
|
-
existsSync as
|
|
4340
|
+
existsSync as existsSync5,
|
|
4300
4341
|
lstatSync as lstatSync3,
|
|
4301
|
-
mkdirSync as
|
|
4302
|
-
readFileSync as
|
|
4303
|
-
realpathSync as
|
|
4304
|
-
renameSync as
|
|
4342
|
+
mkdirSync as mkdirSync6,
|
|
4343
|
+
readFileSync as readFileSync5,
|
|
4344
|
+
realpathSync as realpathSync4,
|
|
4345
|
+
renameSync as renameSync5,
|
|
4305
4346
|
statSync,
|
|
4306
4347
|
symlinkSync as symlinkSync2,
|
|
4307
4348
|
unlinkSync,
|
|
4308
|
-
writeFileSync as
|
|
4349
|
+
writeFileSync as writeFileSync6
|
|
4309
4350
|
} from "fs";
|
|
4310
|
-
import { dirname as
|
|
4351
|
+
import { dirname as dirname7, isAbsolute as isAbsolute2, join as join5, resolve as resolve4 } from "path";
|
|
4311
4352
|
import { isIP as isIP5 } from "net";
|
|
4312
4353
|
|
|
4313
4354
|
// src/metal-isolation.ts
|
|
4314
|
-
import { mkdirSync as
|
|
4355
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
4315
4356
|
import { join as join4 } from "path";
|
|
4316
4357
|
|
|
4317
4358
|
// src/metal-provision.ts
|
|
4318
|
-
import { dirname as
|
|
4359
|
+
import { dirname as dirname6, isAbsolute, join as join3 } from "path";
|
|
4319
4360
|
import { isIP as isIP4 } from "net";
|
|
4320
4361
|
|
|
4321
4362
|
// src/ubuntu.ts
|
|
@@ -4503,16 +4544,16 @@ async function applyMetalIsolation(profile, exec = defaultExec) {
|
|
|
4503
4544
|
validateMetalProfile(profile);
|
|
4504
4545
|
await requireGuestsInSlice(exec);
|
|
4505
4546
|
const unitDir = profile.unitDir;
|
|
4506
|
-
|
|
4507
|
-
|
|
4547
|
+
mkdirSync5(unitDir, { recursive: true });
|
|
4548
|
+
writeFileSync5(join4(unitDir, "forgezero-guests.slice"), metalGuestSliceUnit(profile), { mode: 420 });
|
|
4508
4549
|
for (const unit of ["system.slice", "user.slice"]) {
|
|
4509
4550
|
const directory = join4(unitDir, `${unit}.d`);
|
|
4510
|
-
|
|
4511
|
-
|
|
4551
|
+
mkdirSync5(directory, { recursive: true });
|
|
4552
|
+
writeFileSync5(join4(directory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "slice"), { mode: 420 });
|
|
4512
4553
|
}
|
|
4513
4554
|
const initDirectory = join4(unitDir, "init.scope.d");
|
|
4514
|
-
|
|
4515
|
-
|
|
4555
|
+
mkdirSync5(initDirectory, { recursive: true });
|
|
4556
|
+
writeFileSync5(join4(initDirectory, "50-forgezero-housekeeping.conf"), metalHousekeepingDropIn(profile, "scope"), { mode: 420 });
|
|
4516
4557
|
await checked3(exec, ["systemctl", "daemon-reload"]);
|
|
4517
4558
|
await requireGuestsInSlice(exec);
|
|
4518
4559
|
const properties = [`AllowedCPUs=${profile.housekeepingCpus}`];
|
|
@@ -4631,7 +4672,7 @@ function validateMetalBootstrapConfig(config) {
|
|
|
4631
4672
|
throw new MetalBootstrapError("metal bootstrap uses fixed state, seed, and systemd unit directories");
|
|
4632
4673
|
}
|
|
4633
4674
|
const image = config.profile.images[Object.keys(config.profile.images)[0]];
|
|
4634
|
-
if (!image.path.startsWith("/var/lib/forgezero/images/") ||
|
|
4675
|
+
if (!image.path.startsWith("/var/lib/forgezero/images/") || resolve4(image.path) !== image.path || /[\0\r\n]/.test(image.path)) {
|
|
4635
4676
|
throw new MetalBootstrapError("the pinned guest image must use the fixed image directory");
|
|
4636
4677
|
}
|
|
4637
4678
|
if (config.profile.bunVersion !== SUPPORTED_BUN_VERSION || config.profile.bunReleaseSha256 !== SUPPORTED_BUN_RELEASE_SHA256 || config.profile.agentVersion !== VERSION) {
|
|
@@ -4666,11 +4707,11 @@ function validateMetalBootstrapConfig(config) {
|
|
|
4666
4707
|
return config;
|
|
4667
4708
|
}
|
|
4668
4709
|
function validateOwnerOnlyPath(path, requireRootOwner) {
|
|
4669
|
-
if (!isAbsolute2(path) ||
|
|
4710
|
+
if (!isAbsolute2(path) || resolve4(path) !== path || path.includes("/../")) {
|
|
4670
4711
|
throw new MetalBootstrapError("private bootstrap paths must be canonical absolute paths");
|
|
4671
4712
|
}
|
|
4672
4713
|
const metadata = lstatSync3(path);
|
|
4673
|
-
if (!metadata.isFile() || metadata.isSymbolicLink() ||
|
|
4714
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || realpathSync4(path) !== path) {
|
|
4674
4715
|
throw new MetalBootstrapError("private bootstrap path must be a regular non-symlink file");
|
|
4675
4716
|
}
|
|
4676
4717
|
if ((metadata.mode & 63) !== 0)
|
|
@@ -4687,7 +4728,7 @@ function readMetalBootstrapConfig(path) {
|
|
|
4687
4728
|
throw new MetalBootstrapError("metal bootstrap config size is invalid");
|
|
4688
4729
|
let parsed;
|
|
4689
4730
|
try {
|
|
4690
|
-
parsed = JSON.parse(
|
|
4731
|
+
parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
4691
4732
|
} catch {
|
|
4692
4733
|
throw new MetalBootstrapError("metal bootstrap config is not valid JSON");
|
|
4693
4734
|
}
|
|
@@ -4720,12 +4761,12 @@ function planMetalBootstrap(config) {
|
|
|
4720
4761
|
};
|
|
4721
4762
|
}
|
|
4722
4763
|
var atomicWrite = (path, body, mode) => {
|
|
4723
|
-
|
|
4764
|
+
mkdirSync6(dirname7(path), { recursive: true, mode: 493 });
|
|
4724
4765
|
const temporary = `${path}.next-${process.pid}`;
|
|
4725
|
-
|
|
4766
|
+
writeFileSync6(temporary, body, { mode, flag: "wx" });
|
|
4726
4767
|
chmodSync3(temporary, mode);
|
|
4727
4768
|
chownSync(temporary, 0, 0);
|
|
4728
|
-
|
|
4769
|
+
renameSync5(temporary, path);
|
|
4729
4770
|
};
|
|
4730
4771
|
var validateAgentSourcePath = (source) => {
|
|
4731
4772
|
if (!isAbsolute2(source) || !lstatSync3(source).isFile() || lstatSync3(source).isSymbolicLink()) {
|
|
@@ -4892,11 +4933,11 @@ WantedBy=multi-user.target
|
|
|
4892
4933
|
var installAgentBinary = (source, version) => {
|
|
4893
4934
|
validateAgentSourcePath(source);
|
|
4894
4935
|
const release = `/opt/forgezero/agent/versions/${version}/dist`;
|
|
4895
|
-
|
|
4936
|
+
mkdirSync6(release, { recursive: true, mode: 493 });
|
|
4896
4937
|
copyFileSync2(source, join5(release, "fz-agent.js"));
|
|
4897
4938
|
chmodSync3(join5(release, "fz-agent.js"), 493);
|
|
4898
4939
|
chownSync(join5(release, "fz-agent.js"), 0, 0);
|
|
4899
|
-
|
|
4940
|
+
mkdirSync6("/opt/forgezero/agent", { recursive: true, mode: 493 });
|
|
4900
4941
|
for (const [link, target] of [
|
|
4901
4942
|
["/opt/forgezero/agent/current.next", `versions/${version}`],
|
|
4902
4943
|
[AGENT_PATH, "/opt/forgezero/agent/current/dist/fz-agent.js"]
|
|
@@ -4906,7 +4947,7 @@ var installAgentBinary = (source, version) => {
|
|
|
4906
4947
|
} catch {}
|
|
4907
4948
|
symlinkSync2(target, link);
|
|
4908
4949
|
if (link.endsWith("current.next"))
|
|
4909
|
-
|
|
4950
|
+
renameSync5(link, "/opt/forgezero/agent/current");
|
|
4910
4951
|
}
|
|
4911
4952
|
};
|
|
4912
4953
|
var preflight = async (config, exec) => {
|
|
@@ -4928,10 +4969,10 @@ var preflight = async (config, exec) => {
|
|
|
4928
4969
|
]);
|
|
4929
4970
|
await runChecked(exec, ["/usr/sbin/vgs", config.profile.volumeGroup]);
|
|
4930
4971
|
await runChecked(exec, ["/usr/sbin/ip", "link", "show", config.profile.bridge]);
|
|
4931
|
-
if (!
|
|
4972
|
+
if (!existsSync5("/dev/kvm"))
|
|
4932
4973
|
throw new MetalBootstrapError("/dev/kvm is required");
|
|
4933
4974
|
if (config.profile.confidential) {
|
|
4934
|
-
if (!
|
|
4975
|
+
if (!existsSync5("/dev/sev"))
|
|
4935
4976
|
throw new MetalBootstrapError("/dev/sev is required by the confidential profile");
|
|
4936
4977
|
await runChecked(exec, ["/usr/bin/qemu-system-x86_64", "-object", "sev-snp-guest,help"]);
|
|
4937
4978
|
}
|
|
@@ -4943,16 +4984,16 @@ var assertSupportedMetalHost = () => {
|
|
|
4943
4984
|
if (process.platform !== "linux" || process.arch !== "x64") {
|
|
4944
4985
|
throw new MetalBootstrapError("metal bootstrap supports only Ubuntu 26.04 x86_64 hosts");
|
|
4945
4986
|
}
|
|
4946
|
-
const release =
|
|
4987
|
+
const release = readFileSync5("/etc/os-release", "utf8");
|
|
4947
4988
|
if (!/^ID=ubuntu$/m.test(release) || !/^VERSION_ID="?26\.04"?$/m.test(release)) {
|
|
4948
4989
|
throw new MetalBootstrapError("metal bootstrap supports only Ubuntu 26.04 x86_64 hosts");
|
|
4949
4990
|
}
|
|
4950
4991
|
};
|
|
4951
4992
|
var ensurePinnedGuestImage = async (config, exec) => {
|
|
4952
4993
|
const image = config.profile.images[SUPPORTED_GUEST_IMAGE.key];
|
|
4953
|
-
if (
|
|
4994
|
+
if (existsSync5(image.path))
|
|
4954
4995
|
return;
|
|
4955
|
-
|
|
4996
|
+
mkdirSync6(dirname7(image.path), { recursive: true, mode: 493 });
|
|
4956
4997
|
const temporary = `${image.path}.next-${process.pid}`;
|
|
4957
4998
|
try {
|
|
4958
4999
|
await runChecked(exec, [
|
|
@@ -4971,7 +5012,7 @@ var ensurePinnedGuestImage = async (config, exec) => {
|
|
|
4971
5012
|
throw new MetalBootstrapError("downloaded guest image digest mismatch");
|
|
4972
5013
|
chmodSync3(temporary, 292);
|
|
4973
5014
|
chownSync(temporary, 0, 0);
|
|
4974
|
-
|
|
5015
|
+
renameSync5(temporary, image.path);
|
|
4975
5016
|
} catch (cause) {
|
|
4976
5017
|
try {
|
|
4977
5018
|
unlinkSync(temporary);
|
|
@@ -4984,11 +5025,11 @@ async function applyMetalBootstrap(config, options) {
|
|
|
4984
5025
|
if ((options.getuid ?? process.getuid)?.() !== 0)
|
|
4985
5026
|
throw new MetalBootstrapError("fz bootstrap metal --apply must run as root");
|
|
4986
5027
|
assertSupportedMetalHost();
|
|
4987
|
-
if (
|
|
5028
|
+
if (existsSync5(STATE_PATH2) && !options.repair)
|
|
4988
5029
|
throw new MetalBootstrapError("metal host is already initialized; use explicit repair");
|
|
4989
5030
|
if (config.agentSeedFile)
|
|
4990
5031
|
validateOwnerOnlyPath(config.agentSeedFile, true);
|
|
4991
|
-
if (config.agentSeedFile &&
|
|
5032
|
+
if (config.agentSeedFile && existsSync5(SEED_CREDENTIAL_PATH)) {
|
|
4992
5033
|
throw new MetalBootstrapError("repair refuses replacement seed material while the sealed metal identity exists");
|
|
4993
5034
|
}
|
|
4994
5035
|
validateAgentSourcePath(options.agentSourcePath);
|
|
@@ -5015,9 +5056,9 @@ async function applyMetalBootstrap(config, options) {
|
|
|
5015
5056
|
await preflight(config, exec);
|
|
5016
5057
|
await ensureAccount(exec);
|
|
5017
5058
|
installAgentBinary(options.agentSourcePath, config.profile.agentVersion);
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5059
|
+
mkdirSync6("/etc/forgezero/creds", { recursive: true, mode: 448 });
|
|
5060
|
+
mkdirSync6(config.profile.stateDir, { recursive: true, mode: 448 });
|
|
5061
|
+
mkdirSync6(config.profile.seedDir, { recursive: true, mode: 448 });
|
|
5021
5062
|
const persistedProfile = {
|
|
5022
5063
|
...config.profile,
|
|
5023
5064
|
metalHostname: config.metalHostname,
|
|
@@ -5026,8 +5067,8 @@ async function applyMetalBootstrap(config, options) {
|
|
|
5026
5067
|
};
|
|
5027
5068
|
atomicWrite(PROFILE_PATH, `${JSON.stringify(persistedProfile, null, 2)}
|
|
5028
5069
|
`, 384);
|
|
5029
|
-
if (!
|
|
5030
|
-
const seed = config.agentSeedFile ?
|
|
5070
|
+
if (!existsSync5(SEED_CREDENTIAL_PATH)) {
|
|
5071
|
+
const seed = config.agentSeedFile ? readFileSync5(config.agentSeedFile, "utf8").trim() : randomBytes4(32).toString("base64url");
|
|
5031
5072
|
if (seed.length < 32 || /[\0\r\n]/.test(seed))
|
|
5032
5073
|
throw new MetalBootstrapError("metal Agent seed is invalid");
|
|
5033
5074
|
await runChecked(exec, ["/usr/bin/systemd-creds", "encrypt", "--name=metal-agent-seed", "-", SEED_CREDENTIAL_PATH], `${seed}
|
|
@@ -5098,13 +5139,13 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
5098
5139
|
const problems = [];
|
|
5099
5140
|
let profileValid = false, imageVerified = false, metalHostname, profileSha256;
|
|
5100
5141
|
let profileMode = null;
|
|
5101
|
-
if (
|
|
5142
|
+
if (existsSync5(PROFILE_PATH)) {
|
|
5102
5143
|
try {
|
|
5103
5144
|
const metadata = statSync(PROFILE_PATH);
|
|
5104
5145
|
profileMode = metadata.mode & 511;
|
|
5105
5146
|
if (profileMode !== 384 || metadata.uid !== 0)
|
|
5106
5147
|
problems.push("metal profile is not root-owned mode 0600");
|
|
5107
|
-
const persisted = JSON.parse(
|
|
5148
|
+
const persisted = JSON.parse(readFileSync5(PROFILE_PATH, "utf8"));
|
|
5108
5149
|
const { metalHostname: profileHostname, hostTelemetryEndpoint, hostTelemetryUnit, ...profile } = persisted;
|
|
5109
5150
|
validateMetalProfile(profile);
|
|
5110
5151
|
profileValid = true;
|
|
@@ -5137,7 +5178,7 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
5137
5178
|
problems.push("local OTLP metrics receiver did not accept a proof request");
|
|
5138
5179
|
}
|
|
5139
5180
|
const image = profile.images[Object.keys(profile.images)[0]];
|
|
5140
|
-
if (
|
|
5181
|
+
if (existsSync5(image.path)) {
|
|
5141
5182
|
const digest = (await exec(["/usr/bin/sha256sum", image.path])).stdout.split(/\s+/)[0];
|
|
5142
5183
|
imageVerified = digest === image.sha256;
|
|
5143
5184
|
}
|
|
@@ -5148,9 +5189,9 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
5148
5189
|
}
|
|
5149
5190
|
} else
|
|
5150
5191
|
problems.push("metal profile is missing");
|
|
5151
|
-
if (
|
|
5192
|
+
if (existsSync5(STATE_PATH2)) {
|
|
5152
5193
|
try {
|
|
5153
|
-
const state = JSON.parse(
|
|
5194
|
+
const state = JSON.parse(readFileSync5(STATE_PATH2, "utf8"));
|
|
5154
5195
|
metalHostname = state.metalHostname;
|
|
5155
5196
|
if (state.role !== "metal" || !metalHostname || state.profileSha256 !== profileSha256) {
|
|
5156
5197
|
problems.push("metal initialized state does not bind the current profile");
|
|
@@ -5166,7 +5207,7 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
5166
5207
|
"forgezero-metal-agent-egress.service",
|
|
5167
5208
|
"forgezero-metal-agent.service"
|
|
5168
5209
|
]) {
|
|
5169
|
-
if (!
|
|
5210
|
+
if (!existsSync5(join5(UNIT_DIRECTORY, unit)))
|
|
5170
5211
|
units[unit] = "missing";
|
|
5171
5212
|
else
|
|
5172
5213
|
units[unit] = (await exec(["/usr/bin/systemctl", "is-active", "--quiet", unit])).exitCode === 0 ? "active" : "inactive";
|
|
@@ -5180,7 +5221,7 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
5180
5221
|
if (!updateSocketReady)
|
|
5181
5222
|
problems.push("Agent update helper socket is not ready");
|
|
5182
5223
|
return {
|
|
5183
|
-
initialized:
|
|
5224
|
+
initialized: existsSync5(STATE_PATH2),
|
|
5184
5225
|
profileValid,
|
|
5185
5226
|
profileMode,
|
|
5186
5227
|
imageVerified,
|
|
@@ -5194,7 +5235,7 @@ async function metalBootstrapStatus(exec = defaultExec2) {
|
|
|
5194
5235
|
|
|
5195
5236
|
// src/operator-bootstrap.ts
|
|
5196
5237
|
import { createHash as createHash3, randomBytes as randomBytes5 } from "crypto";
|
|
5197
|
-
import { lstatSync as lstatSync4, mkdtempSync, readFileSync as
|
|
5238
|
+
import { lstatSync as lstatSync4, mkdtempSync, readFileSync as readFileSync6, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
5198
5239
|
import { isIP as isIP6 } from "net";
|
|
5199
5240
|
import { tmpdir } from "os";
|
|
5200
5241
|
import { basename, join as join6 } from "path";
|
|
@@ -5293,7 +5334,7 @@ var ownerFile = (path, limit, label) => {
|
|
|
5293
5334
|
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.uid !== uid || metadata.nlink !== 1 || (metadata.mode & 63) !== 0 || metadata.size < 1 || metadata.size > limit) {
|
|
5294
5335
|
throw new Error(`${label} must be an owner-only regular file with one link and at most ${limit} bytes`);
|
|
5295
5336
|
}
|
|
5296
|
-
return
|
|
5337
|
+
return readFileSync6(path);
|
|
5297
5338
|
};
|
|
5298
5339
|
var publicIdentity = (path) => {
|
|
5299
5340
|
if (!path.startsWith("/") || /[\r\n]/.test(path))
|
|
@@ -5303,7 +5344,7 @@ var publicIdentity = (path) => {
|
|
|
5303
5344
|
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.uid !== uid || metadata.nlink !== 1 || metadata.size > 16384) {
|
|
5304
5345
|
throw new Error("SSH public key must be a caller-owned regular file with one link");
|
|
5305
5346
|
}
|
|
5306
|
-
const value =
|
|
5347
|
+
const value = readFileSync6(path, "utf8").trim();
|
|
5307
5348
|
if (!/^ssh-(?:ed25519|rsa) [A-Za-z0-9+/]+={0,3}(?: [^\r\n]+)?$/.test(value)) {
|
|
5308
5349
|
throw new Error("SSH public key is malformed");
|
|
5309
5350
|
}
|
|
@@ -5507,7 +5548,7 @@ function writeKnownHosts(request, directory) {
|
|
|
5507
5548
|
if (request.target.jump)
|
|
5508
5549
|
lines.push(`${hostLabel(request.target.jump.address, request.target.jump.port)} ${request.target.jump.hostKey}`);
|
|
5509
5550
|
const path = join6(directory, "known_hosts");
|
|
5510
|
-
|
|
5551
|
+
writeFileSync7(path, `${lines.join(`
|
|
5511
5552
|
`)}
|
|
5512
5553
|
`, { mode: 384, flag: "wx" });
|
|
5513
5554
|
return path;
|
|
@@ -5582,7 +5623,7 @@ function stageConfig(config, directory) {
|
|
|
5582
5623
|
for (const [name, source] of secretSources(config)) {
|
|
5583
5624
|
const bytes = ownerFile(source, SECRET_LIMIT, name);
|
|
5584
5625
|
const local = join6(directory, name);
|
|
5585
|
-
|
|
5626
|
+
writeFileSync7(local, bytes, { mode: 384, flag: "wx" });
|
|
5586
5627
|
staged.push(name);
|
|
5587
5628
|
const remotePath = `${REMOTE_STAGE}/${name}`;
|
|
5588
5629
|
if (name === "cluster-code")
|
|
@@ -5597,7 +5638,7 @@ function stageConfig(config, directory) {
|
|
|
5597
5638
|
rewritten.cloudflareHandoff.handoffFile = remotePath;
|
|
5598
5639
|
}
|
|
5599
5640
|
const path = join6(directory, "platform-config.json");
|
|
5600
|
-
|
|
5641
|
+
writeFileSync7(path, `${JSON.stringify(rewritten, null, 2)}
|
|
5601
5642
|
`, { mode: 384, flag: "wx" });
|
|
5602
5643
|
return { path, files: staged };
|
|
5603
5644
|
}
|
|
@@ -5606,7 +5647,7 @@ function stageMetalConfig(config, directory) {
|
|
|
5606
5647
|
const files = [];
|
|
5607
5648
|
if (config.agentSeedFile) {
|
|
5608
5649
|
const name = "metal-agent-seed";
|
|
5609
|
-
|
|
5650
|
+
writeFileSync7(join6(directory, name), ownerFile(config.agentSeedFile, SECRET_LIMIT, name), {
|
|
5610
5651
|
mode: 384,
|
|
5611
5652
|
flag: "wx"
|
|
5612
5653
|
});
|
|
@@ -5614,7 +5655,7 @@ function stageMetalConfig(config, directory) {
|
|
|
5614
5655
|
files.push(name);
|
|
5615
5656
|
}
|
|
5616
5657
|
const path = join6(directory, "metal-config.json");
|
|
5617
|
-
|
|
5658
|
+
writeFileSync7(path, `${JSON.stringify(rewritten, null, 2)}
|
|
5618
5659
|
`, { mode: 384, flag: "wx" });
|
|
5619
5660
|
return { path, files };
|
|
5620
5661
|
}
|
|
@@ -5626,7 +5667,7 @@ async function verifiedBunArchive(directory, fetcher) {
|
|
|
5626
5667
|
if (createHash3("sha256").update(bytes).digest("hex") !== BUN_RELEASE_SHA256)
|
|
5627
5668
|
throw new Error("pinned Bun checksum mismatch");
|
|
5628
5669
|
const path = join6(directory, "bun.zip");
|
|
5629
|
-
|
|
5670
|
+
writeFileSync7(path, bytes, { mode: 384, flag: "wx" });
|
|
5630
5671
|
return path;
|
|
5631
5672
|
}
|
|
5632
5673
|
async function installPackagedAgent(request, knownHosts, exec, directory, remoteTemp, options) {
|
|
@@ -5636,7 +5677,7 @@ async function installPackagedAgent(request, knownHosts, exec, directory, remote
|
|
|
5636
5677
|
[options.fzGitSshPath ?? fileURLToPath2(new URL("./fz-git-ssh.js", import.meta.url)), "fz-git-ssh.js"]
|
|
5637
5678
|
];
|
|
5638
5679
|
for (const [artifact] of artifacts) {
|
|
5639
|
-
if (!
|
|
5680
|
+
if (!readFileSync6(artifact).length)
|
|
5640
5681
|
throw new Error(`packaged artifact is empty: ${basename(artifact)}`);
|
|
5641
5682
|
}
|
|
5642
5683
|
await remote(exec, request, knownHosts, ["/usr/bin/mkdir", "-m", "0700", remoteTemp], "remote staging");
|
|
@@ -5707,7 +5748,7 @@ async function applyOperatorPlatformBootstrap(request, mode, options = {}) {
|
|
|
5707
5748
|
return;
|
|
5708
5749
|
});
|
|
5709
5750
|
}
|
|
5710
|
-
|
|
5751
|
+
rmSync5(directory, { recursive: true, force: true });
|
|
5711
5752
|
}
|
|
5712
5753
|
}
|
|
5713
5754
|
async function applyOperatorMetalBootstrap(request, mode, options = {}) {
|
|
@@ -5759,7 +5800,7 @@ async function applyOperatorMetalBootstrap(request, mode, options = {}) {
|
|
|
5759
5800
|
return;
|
|
5760
5801
|
});
|
|
5761
5802
|
}
|
|
5762
|
-
|
|
5803
|
+
rmSync5(directory, { recursive: true, force: true });
|
|
5763
5804
|
}
|
|
5764
5805
|
}
|
|
5765
5806
|
export {
|