@forgezero/agent 0.1.103 → 0.1.107
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/agent-heartbeat.js +1 -1
- package/dist/bootstrap-bundle.d.ts +6 -0
- package/dist/bootstrap-bundle.js +41 -0
- package/dist/bootstrap.js +60 -4
- package/dist/database-auth-verify.d.ts +6 -0
- package/dist/deployment-connectivity.d.ts +21 -0
- package/dist/deployment-connectivity.js +117 -21
- package/dist/deployment-pull.d.ts +1 -1
- package/dist/deployment-topology.d.ts +2 -0
- package/dist/deployment-topology.js +2 -0
- package/dist/deployment.d.ts +18 -1
- package/dist/fz-agent.js +251 -34
- package/dist/fz.js +164 -97
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.js +60 -4
- package/dist/platform-bootstrap-runtime.d.ts +1 -1
- package/dist/platform-bootstrap-runtime.js +1 -0
- package/dist/platform-fleet-verification.js +129 -26
- package/dist/platform-launch-env.d.ts +19 -0
- package/dist/provision.js +120 -24
- package/dist/software-helper.js +117 -21
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
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.
|
|
4637
|
+
}, VERSION = "0.1.20";
|
|
4638
4638
|
var init_dist = __esm(() => {
|
|
4639
4639
|
init_identity();
|
|
4640
4640
|
VaultError = class VaultError extends Error {
|
|
@@ -4810,9 +4810,9 @@ async function spawnWith(command, env, report = () => {}, options = {}) {
|
|
|
4810
4810
|
}
|
|
4811
4811
|
|
|
4812
4812
|
// src/cli/index.ts
|
|
4813
|
-
import { existsSync as existsSync13, lstatSync as
|
|
4813
|
+
import { existsSync as existsSync13, lstatSync as lstatSync11, mkdirSync as mkdirSync13, readFileSync as readFileSync17, renameSync as renameSync10, rmSync as rmSync8, writeFileSync as writeFileSync13 } from "fs";
|
|
4814
4814
|
import { randomBytes as randomBytes10 } from "crypto";
|
|
4815
|
-
import { basename as basename3, dirname as dirname14, isAbsolute as isAbsolute7, join as join13, resolve as
|
|
4815
|
+
import { basename as basename3, dirname as dirname14, isAbsolute as isAbsolute7, join as join13, resolve as resolve13 } from "path";
|
|
4816
4816
|
|
|
4817
4817
|
// src/process-input.ts
|
|
4818
4818
|
async function writeAndCloseProcessInput(input, value) {
|
|
@@ -4841,7 +4841,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
4841
4841
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
4842
4842
|
|
|
4843
4843
|
// src/version.ts
|
|
4844
|
-
var VERSION2 = "0.1.
|
|
4844
|
+
var VERSION2 = "0.1.107";
|
|
4845
4845
|
|
|
4846
4846
|
// src/software.ts
|
|
4847
4847
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -12496,6 +12496,7 @@ function platformApiCredentialSpecs(options) {
|
|
|
12496
12496
|
];
|
|
12497
12497
|
return [
|
|
12498
12498
|
{ name: "arangodb-jwt", encryptedPath: "/etc/forgezero/creds/arangodb-jwt.cred", required: true },
|
|
12499
|
+
{ name: "arangodb-root-password", encryptedPath: "/etc/forgezero/creds/arangodb-root-password.cred", required: true },
|
|
12499
12500
|
{ name: "seed-sync-root", encryptedPath: "/etc/forgezero/creds/seed-sync-root.cred", required: true },
|
|
12500
12501
|
...optional.filter(([, present]) => present).map(([name]) => ({
|
|
12501
12502
|
name,
|
|
@@ -14079,6 +14080,46 @@ async function buildBootstrapBundle(input, exec = run) {
|
|
|
14079
14080
|
throw cause;
|
|
14080
14081
|
}
|
|
14081
14082
|
}
|
|
14083
|
+
async function refreshBootstrapBundle(input, exec = run) {
|
|
14084
|
+
const outputPath = resolve6(input.outputPath);
|
|
14085
|
+
const manifestPath = `${outputPath}.json`;
|
|
14086
|
+
if (existsSync8(outputPath) !== existsSync8(manifestPath)) {
|
|
14087
|
+
throw new Error("bootstrap bundle and manifest must either both exist or both be absent");
|
|
14088
|
+
}
|
|
14089
|
+
const candidatePath = `${outputPath}.candidate.${process.pid}.${randomBytes6(6).toString("hex")}`;
|
|
14090
|
+
const candidate = await buildBootstrapBundle({ ...input, outputPath: candidatePath }, exec);
|
|
14091
|
+
try {
|
|
14092
|
+
if (!existsSync8(outputPath)) {
|
|
14093
|
+
renameSync7(candidate.bundlePath, outputPath);
|
|
14094
|
+
renameSync7(candidate.manifestPath, manifestPath);
|
|
14095
|
+
return readBootstrapBundle(outputPath);
|
|
14096
|
+
}
|
|
14097
|
+
const current2 = await readBootstrapBundle(outputPath);
|
|
14098
|
+
if (current2.manifest.branch === candidate.manifest.branch && current2.manifest.revision === candidate.manifest.revision) {
|
|
14099
|
+
rmSync5(candidate.bundlePath, { force: true });
|
|
14100
|
+
rmSync5(candidate.manifestPath, { force: true });
|
|
14101
|
+
return current2;
|
|
14102
|
+
}
|
|
14103
|
+
const archivePath = `${outputPath}.before-${current2.manifest.revision.slice(0, 7)}-${Date.now()}`;
|
|
14104
|
+
const archiveManifestPath = `${archivePath}.json`;
|
|
14105
|
+
renameSync7(outputPath, archivePath);
|
|
14106
|
+
renameSync7(manifestPath, archiveManifestPath);
|
|
14107
|
+
try {
|
|
14108
|
+
renameSync7(candidate.bundlePath, outputPath);
|
|
14109
|
+
renameSync7(candidate.manifestPath, manifestPath);
|
|
14110
|
+
return readBootstrapBundle(outputPath);
|
|
14111
|
+
} catch (cause) {
|
|
14112
|
+
rmSync5(outputPath, { force: true });
|
|
14113
|
+
rmSync5(manifestPath, { force: true });
|
|
14114
|
+
renameSync7(archivePath, outputPath);
|
|
14115
|
+
renameSync7(archiveManifestPath, manifestPath);
|
|
14116
|
+
throw cause;
|
|
14117
|
+
}
|
|
14118
|
+
} finally {
|
|
14119
|
+
rmSync5(candidate.bundlePath, { force: true });
|
|
14120
|
+
rmSync5(candidate.manifestPath, { force: true });
|
|
14121
|
+
}
|
|
14122
|
+
}
|
|
14082
14123
|
|
|
14083
14124
|
// src/bootstrap.ts
|
|
14084
14125
|
var PLATFORM_BOOTSTRAP_PROFILES = [
|
|
@@ -14199,6 +14240,7 @@ var STATE_PATH = BOOTSTRAP_STATE_PATH;
|
|
|
14199
14240
|
var INTENT_PATH = "/var/lib/forgezero/bootstrap.intent.json";
|
|
14200
14241
|
var CREDS = "/etc/forgezero/creds";
|
|
14201
14242
|
var JWT_CREDENTIAL = `${CREDS}/arangodb-jwt.cred`;
|
|
14243
|
+
var ARANGO_ROOT_CREDENTIAL = `${CREDS}/arangodb-root-password.cred`;
|
|
14202
14244
|
var ENROL_CREDENTIAL = `${CREDS}/enrol-token.cred`;
|
|
14203
14245
|
var TUNNEL_CREDENTIAL = `${CREDS}/CF_TUNNEL_CONNECTOR_TOKEN.cred`;
|
|
14204
14246
|
var CF_API_CREDENTIAL = `${CREDS}/CF_API_TOKEN.cred`;
|
|
@@ -14484,7 +14526,7 @@ WantedBy=multi-user.target
|
|
|
14484
14526
|
function databaseVerifyUnit(config) {
|
|
14485
14527
|
const { address } = config.database;
|
|
14486
14528
|
return `[Unit]
|
|
14487
|
-
Description=
|
|
14529
|
+
Description=Secure and verify ForgeZero ArangoDB Community 3.11.14 writable Coordinator
|
|
14488
14530
|
Requires=forgezero-db.service
|
|
14489
14531
|
After=forgezero-db.service
|
|
14490
14532
|
PartOf=forgezero-db.service
|
|
@@ -14494,7 +14536,9 @@ Type=oneshot
|
|
|
14494
14536
|
User=arangodb
|
|
14495
14537
|
Group=arangodb
|
|
14496
14538
|
LoadCredentialEncrypted=arangodb-jwt:${JWT_CREDENTIAL}
|
|
14497
|
-
|
|
14539
|
+
LoadCredentialEncrypted=arangodb-root-password:${ARANGO_ROOT_CREDENTIAL}
|
|
14540
|
+
ExecStart=/usr/bin/arangosh --server.endpoint tcp://${unitEscape(address)}:8529 --server.jwt-secret-keyfile %d/arangodb-jwt --javascript.execute-string 'const c=require("@arangodb").db._connection;let last;let ok=false;for(let i=0;i<90;i++){try{const v=c.GET("/_api/version?details=true");const s=c.GET("/_admin/status");const m=c.GET("/_admin/server/mode");if(!v.error&&v.version==="3.11.14"&&v.details&&v.details.license==="community"&&!s.error&&s.serverInfo&&s.serverInfo.role==="COORDINATOR"&&!m.error&&m.mode==="default"){ok=true;break;}last={v,s,m};}catch(e){last=String(e);}require("internal").wait(2);}if(!ok)throw new Error("writable Community Coordinator verification failed: "+JSON.stringify(last));const password=require("fs").read("%d/arangodb-root-password").trim();if(password.length<48)throw new Error("ArangoDB root credential is malformed");require("@arangodb/users").update("root",password,true);'
|
|
14541
|
+
ExecStart=/usr/local/bin/fz-agent database-auth-verify --endpoint=http://${unitEscape(address)}:8529
|
|
14498
14542
|
RemainAfterExit=yes
|
|
14499
14543
|
TimeoutStartSec=200
|
|
14500
14544
|
NoNewPrivileges=true
|
|
@@ -14961,6 +15005,11 @@ async function bootstrapStatus(host = localBootstrapHost()) {
|
|
|
14961
15005
|
if (nginx.exitCode !== 0)
|
|
14962
15006
|
problems.push("nginx configuration is invalid");
|
|
14963
15007
|
if (state.databaseRole !== "none") {
|
|
15008
|
+
for (const credential of [JWT_CREDENTIAL, ARANGO_ROOT_CREDENTIAL]) {
|
|
15009
|
+
services[credential] = host.exists(credential);
|
|
15010
|
+
if (!services[credential])
|
|
15011
|
+
problems.push(`${credential} is missing`);
|
|
15012
|
+
}
|
|
14964
15013
|
const unitPath = "/etc/systemd/system/forgezero-db.service";
|
|
14965
15014
|
const expectsNoAgency = state.databaseAgency === "none";
|
|
14966
15015
|
const unitHasNoAgency = host.exists(unitPath) && host.read(unitPath).includes("--cluster.start-agent=false");
|
|
@@ -15195,6 +15244,9 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
|
|
|
15195
15244
|
if (!host.exists(JWT_CREDENTIAL)) {
|
|
15196
15245
|
await seal(host, "arangodb-jwt", JWT_CREDENTIAL, derive(root, "forgezero/cluster/arangodb-jwt/v1"));
|
|
15197
15246
|
}
|
|
15247
|
+
if (!host.exists(ARANGO_ROOT_CREDENTIAL)) {
|
|
15248
|
+
await seal(host, "arangodb-root-password", ARANGO_ROOT_CREDENTIAL, `fzr_${derive(root, "forgezero/cluster/arangodb-root-password/v1")}`);
|
|
15249
|
+
}
|
|
15198
15250
|
await seal(host, "seed-sync-root", SEED_CREDENTIAL, derive(root, "forgezero/cluster/seed-mesh/v1"));
|
|
15199
15251
|
await seal(host, "backup-recovery-root", BACKUP_RECOVERY_CREDENTIAL, derive(root, "forgezero/backup/recovery-root/v1"));
|
|
15200
15252
|
const emailCredentialName = config.runtime.environment.email?.provider === "smtp" ? "fz_smtp.password" : config.runtime.environment.email?.provider === "jetemail" ? "fz_jetemail.apiKey" : undefined;
|
|
@@ -15478,7 +15530,11 @@ function strictBootstrapDocument(value) {
|
|
|
15478
15530
|
], "runtime environment");
|
|
15479
15531
|
const environment = runtime.environment;
|
|
15480
15532
|
if (environment.initialInventory !== undefined) {
|
|
15481
|
-
const inventory = exactKeys2(environment.initialInventory, ["metalHostname", "region", "computes", "attestation", "deployment"], "initial inventory");
|
|
15533
|
+
const inventory = exactKeys2(environment.initialInventory, ["metalHostname", "metalIdentity", "region", "computes", "attestation", "deployment"], "initial inventory");
|
|
15534
|
+
if (inventory.metalIdentity !== undefined) {
|
|
15535
|
+
const identity = exactKeys2(inventory.metalIdentity, ["nodeKey", "publicKeys"], "initial Metal identity");
|
|
15536
|
+
exactKeys2(identity.publicKeys, ["ed25519", "mlDsa"], "initial Metal public keys");
|
|
15537
|
+
}
|
|
15482
15538
|
exactKeys2(inventory.region, ["key", "label", "country", "city", "confidentialCapable"], "initial inventory region");
|
|
15483
15539
|
if (inventory.attestation !== undefined) {
|
|
15484
15540
|
const attestation = exactKeys2(inventory.attestation, ["measurement", "tcbFloor"], "initial attestation evidence");
|
|
@@ -18535,6 +18591,92 @@ function planAppBuild(input) {
|
|
|
18535
18591
|
};
|
|
18536
18592
|
}
|
|
18537
18593
|
|
|
18594
|
+
// src/platform-launch-env.ts
|
|
18595
|
+
import { lstatSync as lstatSync10, readFileSync as readFileSync16 } from "fs";
|
|
18596
|
+
import { resolve as resolve12 } from "path";
|
|
18597
|
+
var booleanValue = (values, name, fallback) => {
|
|
18598
|
+
const raw = values.get(name)?.trim().toLowerCase();
|
|
18599
|
+
if (raw === undefined || raw === "")
|
|
18600
|
+
return fallback;
|
|
18601
|
+
if (raw === "true")
|
|
18602
|
+
return true;
|
|
18603
|
+
if (raw === "false")
|
|
18604
|
+
return false;
|
|
18605
|
+
throw new Error(`${name} must be true or false`);
|
|
18606
|
+
};
|
|
18607
|
+
function readPlatformLaunchEnvironment(path) {
|
|
18608
|
+
const absolute2 = resolve12(path);
|
|
18609
|
+
const stat2 = lstatSync10(absolute2);
|
|
18610
|
+
if (!stat2.isFile() || stat2.isSymbolicLink() || stat2.nlink !== 1 || typeof process.getuid === "function" && stat2.uid !== process.getuid() || (stat2.mode & 63) !== 0) {
|
|
18611
|
+
throw new Error(`bootstrap secret environment must be one owner-only regular file: ${absolute2}`);
|
|
18612
|
+
}
|
|
18613
|
+
const values = new Map;
|
|
18614
|
+
for (const [index, raw] of readFileSync16(absolute2, "utf8").split(/\r?\n/).entries()) {
|
|
18615
|
+
const line = raw.trim();
|
|
18616
|
+
if (!line || line.startsWith("#"))
|
|
18617
|
+
continue;
|
|
18618
|
+
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
18619
|
+
if (!match)
|
|
18620
|
+
throw new Error(`invalid environment assignment at ${absolute2}:${index + 1}`);
|
|
18621
|
+
if (values.has(match[1]))
|
|
18622
|
+
throw new Error(`duplicate environment assignment ${match[1]} at ${absolute2}:${index + 1}`);
|
|
18623
|
+
let value = match[2].trim();
|
|
18624
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
18625
|
+
value = value.slice(1, -1);
|
|
18626
|
+
}
|
|
18627
|
+
values.set(match[1], value);
|
|
18628
|
+
}
|
|
18629
|
+
const requiredValue = (name) => {
|
|
18630
|
+
const value = values.get(name)?.trim();
|
|
18631
|
+
if (!value)
|
|
18632
|
+
throw new Error(`bootstrap secret environment is missing ${name}`);
|
|
18633
|
+
return value;
|
|
18634
|
+
};
|
|
18635
|
+
const provider = requiredValue("FZ_EMAIL_PROVIDER");
|
|
18636
|
+
if (provider !== "smtp" && provider !== "jetemail")
|
|
18637
|
+
throw new Error("FZ_EMAIL_PROVIDER must be smtp or jetemail");
|
|
18638
|
+
const smtpPort = provider === "smtp" ? Number(requiredValue("FZ_SMTP_PORT")) : undefined;
|
|
18639
|
+
if (smtpPort !== undefined && (!Number.isInteger(smtpPort) || smtpPort < 1 || smtpPort > 65535)) {
|
|
18640
|
+
throw new Error("FZ_SMTP_PORT must be an integer from 1 through 65535");
|
|
18641
|
+
}
|
|
18642
|
+
const githubEnabled = booleanValue(values, "FZ_GITHUB_ENABLED", false);
|
|
18643
|
+
const githubApp = githubEnabled ? {
|
|
18644
|
+
clientId: requiredValue("FZ_GITHUB_CLIENT_ID"),
|
|
18645
|
+
appId: requiredValue("FZ_GITHUB_APP_ID"),
|
|
18646
|
+
slug: requiredValue("FZ_GITHUB_APP_SLUG")
|
|
18647
|
+
} : undefined;
|
|
18648
|
+
const owner = provider === "jetemail" ? {
|
|
18649
|
+
custodianEmail: requiredValue("FZ_BOOTSTRAP_CUSTODIAN_EMAIL"),
|
|
18650
|
+
email: {
|
|
18651
|
+
provider,
|
|
18652
|
+
from: requiredValue("FZ_EMAIL_FROM"),
|
|
18653
|
+
eu: booleanValue(values, "FZ_JETEMAIL_EU", false)
|
|
18654
|
+
},
|
|
18655
|
+
...githubApp ? { githubApp } : {}
|
|
18656
|
+
} : {
|
|
18657
|
+
custodianEmail: requiredValue("FZ_BOOTSTRAP_CUSTODIAN_EMAIL"),
|
|
18658
|
+
email: {
|
|
18659
|
+
provider,
|
|
18660
|
+
from: requiredValue("FZ_EMAIL_FROM"),
|
|
18661
|
+
host: requiredValue("FZ_SMTP_HOST"),
|
|
18662
|
+
port: smtpPort,
|
|
18663
|
+
user: requiredValue("FZ_SMTP_USER")
|
|
18664
|
+
},
|
|
18665
|
+
...githubApp ? { githubApp } : {}
|
|
18666
|
+
};
|
|
18667
|
+
return {
|
|
18668
|
+
owner,
|
|
18669
|
+
emailSecret: requiredValue(provider === "jetemail" ? "FZ_JETEMAIL_API_KEY" : "FZ_SMTP_PASSWORD"),
|
|
18670
|
+
cloudflareTunnelToken: requiredValue("CF_TUNNEL_TOKEN"),
|
|
18671
|
+
cloudflareApiToken: requiredValue("CF_API_TOKEN"),
|
|
18672
|
+
...githubEnabled ? {
|
|
18673
|
+
githubClientSecret: requiredValue("FZ_GITHUB_CLIENT_SECRET"),
|
|
18674
|
+
githubPrivateKeyBase64: requiredValue("FZ_GITHUB_PRIVATE_KEY_BASE64"),
|
|
18675
|
+
githubWebhookSecret: requiredValue("FZ_GITHUB_WEBHOOK_SECRET")
|
|
18676
|
+
} : {}
|
|
18677
|
+
};
|
|
18678
|
+
}
|
|
18679
|
+
|
|
18538
18680
|
// src/cli/index.ts
|
|
18539
18681
|
var DEFAULT_MODE = (THRESHOLD_MODES.find((mode) => mode.threshold === 1 && mode.total === 1) ?? THRESHOLD_MODES[0]).id;
|
|
18540
18682
|
var PACKAGED_AGENT_BIN2 = operatorPackagedAgentPath();
|
|
@@ -18847,7 +18989,7 @@ async function api(options, path, init) {
|
|
|
18847
18989
|
return { status: response.status, body };
|
|
18848
18990
|
}
|
|
18849
18991
|
function sleep(ms) {
|
|
18850
|
-
return new Promise((
|
|
18992
|
+
return new Promise((resolve14) => setTimeout(resolve14, ms));
|
|
18851
18993
|
}
|
|
18852
18994
|
function browserCommand(url) {
|
|
18853
18995
|
if (process.platform === "darwin")
|
|
@@ -18932,7 +19074,7 @@ function requireSession() {
|
|
|
18932
19074
|
function readOwnerOnlySecret(path, label) {
|
|
18933
19075
|
let metadata;
|
|
18934
19076
|
try {
|
|
18935
|
-
metadata =
|
|
19077
|
+
metadata = lstatSync11(path);
|
|
18936
19078
|
} catch {
|
|
18937
19079
|
throw new Error(`The ${label} file ${path} is unreadable.`);
|
|
18938
19080
|
}
|
|
@@ -18946,7 +19088,7 @@ function readOwnerOnlySecret(path, label) {
|
|
|
18946
19088
|
if (uid !== undefined && uid !== 0 && metadata.uid !== uid) {
|
|
18947
19089
|
throw new Error(`The ${label} file ${path} is not owned by the current user.`);
|
|
18948
19090
|
}
|
|
18949
|
-
const value =
|
|
19091
|
+
const value = readFileSync17(path, "utf8").trim();
|
|
18950
19092
|
if (!value)
|
|
18951
19093
|
throw new Error(`The ${label} file ${path} is empty.`);
|
|
18952
19094
|
return value;
|
|
@@ -19127,7 +19269,7 @@ async function cmdApi(options, args) {
|
|
|
19127
19269
|
let body = undefined;
|
|
19128
19270
|
if (options.data !== undefined && options.dataFile)
|
|
19129
19271
|
throw new Error("Use only one of --data or --data-file.");
|
|
19130
|
-
const encoded = options.data === "-" ? await Bun.stdin.text() : options.dataFile ?
|
|
19272
|
+
const encoded = options.data === "-" ? await Bun.stdin.text() : options.dataFile ? readFileSync17(options.dataFile, "utf8") : options.data;
|
|
19131
19273
|
if (encoded !== undefined)
|
|
19132
19274
|
body = JSON.parse(encoded);
|
|
19133
19275
|
const result = await api(options, `${url.pathname}${url.search}`, { method, body });
|
|
@@ -19306,7 +19448,7 @@ async function cmdAgent(options, args) {
|
|
|
19306
19448
|
out.line();
|
|
19307
19449
|
out.line(" Compatibility SSH deploy public key:");
|
|
19308
19450
|
out.line();
|
|
19309
|
-
out.line(` ${
|
|
19451
|
+
out.line(` ${readFileSync17(gitIdentity.gitPublicKeyPath, "utf8").trim()}`);
|
|
19310
19452
|
out.line();
|
|
19311
19453
|
}
|
|
19312
19454
|
return 0;
|
|
@@ -19346,78 +19488,6 @@ async function bootstrapSecret(question) {
|
|
|
19346
19488
|
throw new Error(`${question} was not provided`);
|
|
19347
19489
|
return value;
|
|
19348
19490
|
}
|
|
19349
|
-
function readPlatformLaunchEnvironment(path) {
|
|
19350
|
-
const absolute2 = resolve12(path);
|
|
19351
|
-
const stat2 = lstatSync10(absolute2);
|
|
19352
|
-
if (!stat2.isFile() || stat2.isSymbolicLink() || stat2.nlink !== 1 || typeof process.getuid === "function" && stat2.uid !== process.getuid() || (stat2.mode & 63) !== 0) {
|
|
19353
|
-
throw new Error(`bootstrap secret environment must be one owner-only regular file: ${absolute2}`);
|
|
19354
|
-
}
|
|
19355
|
-
const values = new Map;
|
|
19356
|
-
for (const [index, raw] of readFileSync16(absolute2, "utf8").split(/\r?\n/).entries()) {
|
|
19357
|
-
const line = raw.trim();
|
|
19358
|
-
if (!line || line.startsWith("#"))
|
|
19359
|
-
continue;
|
|
19360
|
-
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
19361
|
-
if (!match)
|
|
19362
|
-
throw new Error(`invalid environment assignment at ${absolute2}:${index + 1}`);
|
|
19363
|
-
if (values.has(match[1]))
|
|
19364
|
-
throw new Error(`duplicate environment assignment ${match[1]} at ${absolute2}:${index + 1}`);
|
|
19365
|
-
let value = match[2].trim();
|
|
19366
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
19367
|
-
value = value.slice(1, -1);
|
|
19368
|
-
}
|
|
19369
|
-
values.set(match[1], value);
|
|
19370
|
-
}
|
|
19371
|
-
const requiredValue = (name) => {
|
|
19372
|
-
const value = values.get(name)?.trim();
|
|
19373
|
-
if (!value)
|
|
19374
|
-
throw new Error(`bootstrap secret environment is missing ${name}`);
|
|
19375
|
-
return value;
|
|
19376
|
-
};
|
|
19377
|
-
const provider = requiredValue("FZ_EMAIL_PROVIDER");
|
|
19378
|
-
if (provider !== "smtp" && provider !== "jetemail")
|
|
19379
|
-
throw new Error("FZ_EMAIL_PROVIDER must be smtp or jetemail");
|
|
19380
|
-
const smtpPort = provider === "smtp" ? Number(requiredValue("FZ_SMTP_PORT")) : undefined;
|
|
19381
|
-
if (smtpPort !== undefined && (!Number.isInteger(smtpPort) || smtpPort < 1 || smtpPort > 65535)) {
|
|
19382
|
-
throw new Error("FZ_SMTP_PORT must be an integer from 1 through 65535");
|
|
19383
|
-
}
|
|
19384
|
-
const githubEnabled = (values.get("FZ_GITHUB_ENABLED") ?? "false").trim().toLowerCase() === "true";
|
|
19385
|
-
const githubApp = githubEnabled ? {
|
|
19386
|
-
clientId: requiredValue("FZ_GITHUB_CLIENT_ID"),
|
|
19387
|
-
appId: requiredValue("FZ_GITHUB_APP_ID"),
|
|
19388
|
-
slug: requiredValue("FZ_GITHUB_APP_SLUG")
|
|
19389
|
-
} : undefined;
|
|
19390
|
-
const owner = provider === "jetemail" ? {
|
|
19391
|
-
custodianEmail: requiredValue("FZ_BOOTSTRAP_CUSTODIAN_EMAIL"),
|
|
19392
|
-
email: {
|
|
19393
|
-
provider,
|
|
19394
|
-
from: requiredValue("FZ_EMAIL_FROM"),
|
|
19395
|
-
eu: (values.get("FZ_JETEMAIL_EU") ?? "false").trim().toLowerCase() === "true"
|
|
19396
|
-
},
|
|
19397
|
-
...githubApp ? { githubApp } : {}
|
|
19398
|
-
} : {
|
|
19399
|
-
custodianEmail: requiredValue("FZ_BOOTSTRAP_CUSTODIAN_EMAIL"),
|
|
19400
|
-
email: {
|
|
19401
|
-
provider,
|
|
19402
|
-
from: requiredValue("FZ_EMAIL_FROM"),
|
|
19403
|
-
host: requiredValue("FZ_SMTP_HOST"),
|
|
19404
|
-
port: smtpPort,
|
|
19405
|
-
user: requiredValue("FZ_SMTP_USER")
|
|
19406
|
-
},
|
|
19407
|
-
...githubApp ? { githubApp } : {}
|
|
19408
|
-
};
|
|
19409
|
-
return {
|
|
19410
|
-
owner,
|
|
19411
|
-
emailSecret: requiredValue(provider === "jetemail" ? "FZ_JETEMAIL_API_KEY" : "FZ_SMTP_PASSWORD"),
|
|
19412
|
-
cloudflareTunnelToken: requiredValue("CF_TUNNEL_TOKEN"),
|
|
19413
|
-
cloudflareApiToken: requiredValue("CF_API_TOKEN"),
|
|
19414
|
-
...githubEnabled ? {
|
|
19415
|
-
githubClientSecret: requiredValue("FZ_GITHUB_CLIENT_SECRET"),
|
|
19416
|
-
githubPrivateKeyBase64: requiredValue("FZ_GITHUB_PRIVATE_KEY_BASE64"),
|
|
19417
|
-
githubWebhookSecret: requiredValue("FZ_GITHUB_WEBHOOK_SECRET")
|
|
19418
|
-
} : {}
|
|
19419
|
-
};
|
|
19420
|
-
}
|
|
19421
19491
|
async function promptPlatformBootstrapSecrets(config, generatedClusterBootstrapCode) {
|
|
19422
19492
|
return validatePlatformBootstrapSecrets(config, {
|
|
19423
19493
|
clusterBootstrapCode: generatedClusterBootstrapCode ?? await bootstrapSecret("Shared 64-hex cluster bootstrap code"),
|
|
@@ -19493,7 +19563,7 @@ function interactiveMetalBootstrap() {
|
|
|
19493
19563
|
});
|
|
19494
19564
|
}
|
|
19495
19565
|
function writeBootstrapConfig(path, config) {
|
|
19496
|
-
if (!isAbsolute7(path) ||
|
|
19566
|
+
if (!isAbsolute7(path) || resolve13(path) !== path)
|
|
19497
19567
|
throw new Error("--output must be a canonical absolute path");
|
|
19498
19568
|
mkdirSync13(dirname14(path), { recursive: true, mode: 448 });
|
|
19499
19569
|
writeFileSync13(path, `${JSON.stringify(config, null, 2)}
|
|
@@ -19557,11 +19627,11 @@ async function interactiveCloudflareBootstrap() {
|
|
|
19557
19627
|
});
|
|
19558
19628
|
}
|
|
19559
19629
|
function genesisOutputDirectory(path) {
|
|
19560
|
-
if (!isAbsolute7(path) ||
|
|
19630
|
+
if (!isAbsolute7(path) || resolve13(path) !== path)
|
|
19561
19631
|
throw new Error("--output must be a canonical absolute directory");
|
|
19562
19632
|
if (!existsSync13(path))
|
|
19563
19633
|
mkdirSync13(path, { recursive: true, mode: 448 });
|
|
19564
|
-
const metadata =
|
|
19634
|
+
const metadata = lstatSync11(path);
|
|
19565
19635
|
const uid = process.getuid?.();
|
|
19566
19636
|
if (!metadata.isDirectory() || metadata.isSymbolicLink() || (metadata.mode & 63) !== 0 || uid !== undefined && uid !== 0 && metadata.uid !== uid) {
|
|
19567
19637
|
throw new Error("platform genesis output must be an owner-only directory owned by the current user");
|
|
@@ -19772,14 +19842,11 @@ async function prepareForgeZeroPlatformLaunch(environment, outputDirectory, proj
|
|
|
19772
19842
|
if (!existsSync13(path))
|
|
19773
19843
|
throw new Error(`${label} is missing at ${path}`);
|
|
19774
19844
|
}
|
|
19775
|
-
const root =
|
|
19845
|
+
const root = resolve13(projectRoot);
|
|
19776
19846
|
const nestedApi = join13(root, "api");
|
|
19777
19847
|
const repositoryRoot = existsSync13(join13(nestedApi, "package.json")) ? nestedApi : root;
|
|
19778
19848
|
const bundlePath = join13(base, "api.bundle");
|
|
19779
|
-
const bundle =
|
|
19780
|
-
if (bundle.manifest.branch !== profile.branch) {
|
|
19781
|
-
throw new Error(`existing bootstrap bundle is for ${bundle.manifest.branch}, expected ${profile.branch}`);
|
|
19782
|
-
}
|
|
19849
|
+
const bundle = await refreshBootstrapBundle({ repositoryRoot, outputPath: bundlePath, branch: profile.branch });
|
|
19783
19850
|
return writePlatformGenesisFleet(outputDirectory, {
|
|
19784
19851
|
profile,
|
|
19785
19852
|
metalRequestFile,
|
|
@@ -20261,7 +20328,7 @@ async function cmdUnlock(options) {
|
|
|
20261
20328
|
if (options.key || options.userExplicit) {
|
|
20262
20329
|
throw new Error("SSH/user unlock coordinates are not accepted. SSH keys cannot open a WebAuthn-PRF custody envelope; use --phrase-file or --phrase-stdin.");
|
|
20263
20330
|
}
|
|
20264
|
-
const phraseText = options.phraseFile ? readOwnerOnlySecret(options.phraseFile, "recovery phrase") : options.phraseStdin ?
|
|
20331
|
+
const phraseText = options.phraseFile ? readOwnerOnlySecret(options.phraseFile, "recovery phrase") : options.phraseStdin ? readFileSync17(0, "utf8").trim() : "";
|
|
20265
20332
|
const phrase = phraseText ? phraseText.split(/\s+/) : [];
|
|
20266
20333
|
if (phrase.length !== 24) {
|
|
20267
20334
|
throw new Error(`Recovery phrase must contain exactly 24 words; received ${phrase.length}. Use --phrase-file or --phrase-stdin.`);
|
|
@@ -20494,7 +20561,7 @@ function projectFromCheckout(options) {
|
|
|
20494
20561
|
try {
|
|
20495
20562
|
return loadConfig({
|
|
20496
20563
|
cwd: options.projectRoot,
|
|
20497
|
-
readFile: (path) => existsSync13(path) ?
|
|
20564
|
+
readFile: (path) => existsSync13(path) ? readFileSync17(path, "utf8") : undefined
|
|
20498
20565
|
}).config.project;
|
|
20499
20566
|
} catch (cause) {
|
|
20500
20567
|
throw new Error(`No --project was given and the checkout has no usable .fz/config.json: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
@@ -20561,7 +20628,7 @@ async function cmdDeploy(options, args) {
|
|
|
20561
20628
|
throw new Error("typed runtime initialization selects its tested software and attestation from --runtime/--isolation; do not combine --software or --attestation");
|
|
20562
20629
|
}
|
|
20563
20630
|
initializeTypeScriptDeployment(options.projectRoot, {
|
|
20564
|
-
name: options.projectName ?? basename3(
|
|
20631
|
+
name: options.projectName ?? basename3(resolve13(options.projectRoot)),
|
|
20565
20632
|
force: options.force,
|
|
20566
20633
|
runtime: options.deployRuntime,
|
|
20567
20634
|
operatingSystem: options.deployOperatingSystem,
|
|
@@ -20616,7 +20683,7 @@ async function cmdDeploy(options, args) {
|
|
|
20616
20683
|
return problems.length === 0 ? 0 : 1;
|
|
20617
20684
|
}
|
|
20618
20685
|
if (operation === "check" || operation === "sync") {
|
|
20619
|
-
if (existsSync13(
|
|
20686
|
+
if (existsSync13(resolve13(options.projectRoot, DEPLOY_SOURCE_FILE))) {
|
|
20620
20687
|
const expected = await compileDeploymentProject(options.projectRoot, { write: false });
|
|
20621
20688
|
const actual = inspectCompiledDeployment(options.projectRoot);
|
|
20622
20689
|
const current2 = canonicalJson(expected.plan) === canonicalJson(actual.plan);
|
|
@@ -20692,7 +20759,7 @@ async function cmdDeploy(options, args) {
|
|
|
20692
20759
|
branch: options.branch,
|
|
20693
20760
|
cloneUrl: required(options.cloneUrl, "--clone-url"),
|
|
20694
20761
|
sourceAuth,
|
|
20695
|
-
...options.knownHostsFile ? { knownHosts:
|
|
20762
|
+
...options.knownHostsFile ? { knownHosts: readFileSync17(options.knownHostsFile, "utf8") } : {},
|
|
20696
20763
|
projectKey
|
|
20697
20764
|
} });
|
|
20698
20765
|
const pipelineKey2 = String(created.pipelineKey);
|
package/dist/metal-bootstrap.js
CHANGED
|
@@ -366,7 +366,7 @@ function systemdAgentEgressDirectives(loopbackTcpPorts = []) {
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
// src/version.ts
|
|
369
|
-
var VERSION = "0.1.
|
|
369
|
+
var VERSION = "0.1.107";
|
|
370
370
|
|
|
371
371
|
// src/otel-collector.ts
|
|
372
372
|
var FORGEZERO_OTEL_COLLECTOR_UNIT = "forgezero-otel-collector.service";
|
|
@@ -1384,6 +1384,46 @@ async function buildBootstrapBundle(input, exec = run) {
|
|
|
1384
1384
|
throw cause;
|
|
1385
1385
|
}
|
|
1386
1386
|
}
|
|
1387
|
+
async function refreshBootstrapBundle(input, exec = run) {
|
|
1388
|
+
const outputPath = resolve2(input.outputPath);
|
|
1389
|
+
const manifestPath = `${outputPath}.json`;
|
|
1390
|
+
if (existsSync(outputPath) !== existsSync(manifestPath)) {
|
|
1391
|
+
throw new Error("bootstrap bundle and manifest must either both exist or both be absent");
|
|
1392
|
+
}
|
|
1393
|
+
const candidatePath = `${outputPath}.candidate.${process.pid}.${randomBytes(6).toString("hex")}`;
|
|
1394
|
+
const candidate = await buildBootstrapBundle({ ...input, outputPath: candidatePath }, exec);
|
|
1395
|
+
try {
|
|
1396
|
+
if (!existsSync(outputPath)) {
|
|
1397
|
+
renameSync(candidate.bundlePath, outputPath);
|
|
1398
|
+
renameSync(candidate.manifestPath, manifestPath);
|
|
1399
|
+
return readBootstrapBundle(outputPath);
|
|
1400
|
+
}
|
|
1401
|
+
const current = await readBootstrapBundle(outputPath);
|
|
1402
|
+
if (current.manifest.branch === candidate.manifest.branch && current.manifest.revision === candidate.manifest.revision) {
|
|
1403
|
+
rmSync(candidate.bundlePath, { force: true });
|
|
1404
|
+
rmSync(candidate.manifestPath, { force: true });
|
|
1405
|
+
return current;
|
|
1406
|
+
}
|
|
1407
|
+
const archivePath = `${outputPath}.before-${current.manifest.revision.slice(0, 7)}-${Date.now()}`;
|
|
1408
|
+
const archiveManifestPath = `${archivePath}.json`;
|
|
1409
|
+
renameSync(outputPath, archivePath);
|
|
1410
|
+
renameSync(manifestPath, archiveManifestPath);
|
|
1411
|
+
try {
|
|
1412
|
+
renameSync(candidate.bundlePath, outputPath);
|
|
1413
|
+
renameSync(candidate.manifestPath, manifestPath);
|
|
1414
|
+
return readBootstrapBundle(outputPath);
|
|
1415
|
+
} catch (cause) {
|
|
1416
|
+
rmSync(outputPath, { force: true });
|
|
1417
|
+
rmSync(manifestPath, { force: true });
|
|
1418
|
+
renameSync(archivePath, outputPath);
|
|
1419
|
+
renameSync(archiveManifestPath, manifestPath);
|
|
1420
|
+
throw cause;
|
|
1421
|
+
}
|
|
1422
|
+
} finally {
|
|
1423
|
+
rmSync(candidate.bundlePath, { force: true });
|
|
1424
|
+
rmSync(candidate.manifestPath, { force: true });
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1387
1427
|
|
|
1388
1428
|
// src/bootstrap.ts
|
|
1389
1429
|
import { createHash as createHash2, createHmac as createHmac2, createPrivateKey, randomBytes as randomBytes3 } from "crypto";
|
|
@@ -1420,7 +1460,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
1420
1460
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
1421
1461
|
|
|
1422
1462
|
// src/version.ts
|
|
1423
|
-
var VERSION = "0.1.
|
|
1463
|
+
var VERSION = "0.1.107";
|
|
1424
1464
|
|
|
1425
1465
|
// src/software.ts
|
|
1426
1466
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -3347,6 +3387,7 @@ function platformApiCredentialSpecs(options) {
|
|
|
3347
3387
|
];
|
|
3348
3388
|
return [
|
|
3349
3389
|
{ name: "arangodb-jwt", encryptedPath: "/etc/forgezero/creds/arangodb-jwt.cred", required: true },
|
|
3390
|
+
{ name: "arangodb-root-password", encryptedPath: "/etc/forgezero/creds/arangodb-root-password.cred", required: true },
|
|
3350
3391
|
{ name: "seed-sync-root", encryptedPath: "/etc/forgezero/creds/seed-sync-root.cred", required: true },
|
|
3351
3392
|
...optional.filter(([, present]) => present).map(([name]) => ({
|
|
3352
3393
|
name,
|
|
@@ -3814,6 +3855,7 @@ var STATE_PATH = BOOTSTRAP_STATE_PATH;
|
|
|
3814
3855
|
var INTENT_PATH = "/var/lib/forgezero/bootstrap.intent.json";
|
|
3815
3856
|
var CREDS = "/etc/forgezero/creds";
|
|
3816
3857
|
var JWT_CREDENTIAL = `${CREDS}/arangodb-jwt.cred`;
|
|
3858
|
+
var ARANGO_ROOT_CREDENTIAL = `${CREDS}/arangodb-root-password.cred`;
|
|
3817
3859
|
var ENROL_CREDENTIAL = `${CREDS}/enrol-token.cred`;
|
|
3818
3860
|
var TUNNEL_CREDENTIAL = `${CREDS}/CF_TUNNEL_CONNECTOR_TOKEN.cred`;
|
|
3819
3861
|
var CF_API_CREDENTIAL = `${CREDS}/CF_API_TOKEN.cred`;
|
|
@@ -4099,7 +4141,7 @@ WantedBy=multi-user.target
|
|
|
4099
4141
|
function databaseVerifyUnit(config) {
|
|
4100
4142
|
const { address } = config.database;
|
|
4101
4143
|
return `[Unit]
|
|
4102
|
-
Description=
|
|
4144
|
+
Description=Secure and verify ForgeZero ArangoDB Community 3.11.14 writable Coordinator
|
|
4103
4145
|
Requires=forgezero-db.service
|
|
4104
4146
|
After=forgezero-db.service
|
|
4105
4147
|
PartOf=forgezero-db.service
|
|
@@ -4109,7 +4151,9 @@ Type=oneshot
|
|
|
4109
4151
|
User=arangodb
|
|
4110
4152
|
Group=arangodb
|
|
4111
4153
|
LoadCredentialEncrypted=arangodb-jwt:${JWT_CREDENTIAL}
|
|
4112
|
-
|
|
4154
|
+
LoadCredentialEncrypted=arangodb-root-password:${ARANGO_ROOT_CREDENTIAL}
|
|
4155
|
+
ExecStart=/usr/bin/arangosh --server.endpoint tcp://${unitEscape(address)}:8529 --server.jwt-secret-keyfile %d/arangodb-jwt --javascript.execute-string 'const c=require("@arangodb").db._connection;let last;let ok=false;for(let i=0;i<90;i++){try{const v=c.GET("/_api/version?details=true");const s=c.GET("/_admin/status");const m=c.GET("/_admin/server/mode");if(!v.error&&v.version==="3.11.14"&&v.details&&v.details.license==="community"&&!s.error&&s.serverInfo&&s.serverInfo.role==="COORDINATOR"&&!m.error&&m.mode==="default"){ok=true;break;}last={v,s,m};}catch(e){last=String(e);}require("internal").wait(2);}if(!ok)throw new Error("writable Community Coordinator verification failed: "+JSON.stringify(last));const password=require("fs").read("%d/arangodb-root-password").trim();if(password.length<48)throw new Error("ArangoDB root credential is malformed");require("@arangodb/users").update("root",password,true);'
|
|
4156
|
+
ExecStart=/usr/local/bin/fz-agent database-auth-verify --endpoint=http://${unitEscape(address)}:8529
|
|
4113
4157
|
RemainAfterExit=yes
|
|
4114
4158
|
TimeoutStartSec=200
|
|
4115
4159
|
NoNewPrivileges=true
|
|
@@ -4576,6 +4620,11 @@ async function bootstrapStatus(host = localBootstrapHost()) {
|
|
|
4576
4620
|
if (nginx.exitCode !== 0)
|
|
4577
4621
|
problems.push("nginx configuration is invalid");
|
|
4578
4622
|
if (state.databaseRole !== "none") {
|
|
4623
|
+
for (const credential of [JWT_CREDENTIAL, ARANGO_ROOT_CREDENTIAL]) {
|
|
4624
|
+
services[credential] = host.exists(credential);
|
|
4625
|
+
if (!services[credential])
|
|
4626
|
+
problems.push(`${credential} is missing`);
|
|
4627
|
+
}
|
|
4579
4628
|
const unitPath = "/etc/systemd/system/forgezero-db.service";
|
|
4580
4629
|
const expectsNoAgency = state.databaseAgency === "none";
|
|
4581
4630
|
const unitHasNoAgency = host.exists(unitPath) && host.read(unitPath).includes("--cluster.start-agent=false");
|
|
@@ -4810,6 +4859,9 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
|
|
|
4810
4859
|
if (!host.exists(JWT_CREDENTIAL)) {
|
|
4811
4860
|
await seal(host, "arangodb-jwt", JWT_CREDENTIAL, derive(root, "forgezero/cluster/arangodb-jwt/v1"));
|
|
4812
4861
|
}
|
|
4862
|
+
if (!host.exists(ARANGO_ROOT_CREDENTIAL)) {
|
|
4863
|
+
await seal(host, "arangodb-root-password", ARANGO_ROOT_CREDENTIAL, `fzr_${derive(root, "forgezero/cluster/arangodb-root-password/v1")}`);
|
|
4864
|
+
}
|
|
4813
4865
|
await seal(host, "seed-sync-root", SEED_CREDENTIAL, derive(root, "forgezero/cluster/seed-mesh/v1"));
|
|
4814
4866
|
await seal(host, "backup-recovery-root", BACKUP_RECOVERY_CREDENTIAL, derive(root, "forgezero/backup/recovery-root/v1"));
|
|
4815
4867
|
const emailCredentialName = config.runtime.environment.email?.provider === "smtp" ? "fz_smtp.password" : config.runtime.environment.email?.provider === "jetemail" ? "fz_jetemail.apiKey" : undefined;
|
|
@@ -5093,7 +5145,11 @@ function strictBootstrapDocument(value) {
|
|
|
5093
5145
|
], "runtime environment");
|
|
5094
5146
|
const environment = runtime.environment;
|
|
5095
5147
|
if (environment.initialInventory !== undefined) {
|
|
5096
|
-
const inventory = exactKeys(environment.initialInventory, ["metalHostname", "region", "computes", "attestation", "deployment"], "initial inventory");
|
|
5148
|
+
const inventory = exactKeys(environment.initialInventory, ["metalHostname", "metalIdentity", "region", "computes", "attestation", "deployment"], "initial inventory");
|
|
5149
|
+
if (inventory.metalIdentity !== undefined) {
|
|
5150
|
+
const identity = exactKeys(inventory.metalIdentity, ["nodeKey", "publicKeys"], "initial Metal identity");
|
|
5151
|
+
exactKeys(identity.publicKeys, ["ed25519", "mlDsa"], "initial Metal public keys");
|
|
5152
|
+
}
|
|
5097
5153
|
exactKeys(inventory.region, ["key", "label", "country", "city", "confidentialCapable"], "initial inventory region");
|
|
5098
5154
|
if (inventory.attestation !== undefined) {
|
|
5099
5155
|
const attestation = exactKeys(inventory.attestation, ["measurement", "tcbFloor"], "initial attestation evidence");
|
|
@@ -137,7 +137,7 @@ export declare function validatePlatformSharedEnvironment(input: PlatformSharedE
|
|
|
137
137
|
/** Render only non-secret runtime coordinates. Passwords/tokens have no field in this contract. */
|
|
138
138
|
export declare function renderPlatformSharedEnvironment(input: PlatformSharedEnvironment): string;
|
|
139
139
|
export interface SystemdCredentialSpec {
|
|
140
|
-
name: 'arangodb-jwt' | 'seed-sync-root' | 'fz_smtp.password' | 'fz_jetemail.apiKey' | 'fz_github.clientSecret' | 'fz_github.privateKey' | 'fz_github.webhookSecret' | 'CF_API_TOKEN' | 'CF_TUNNEL_TOKEN' | 'REALTIME_PUBLISH_SECRET' | 'REALTIME_TICKET_SECRET';
|
|
140
|
+
name: 'arangodb-jwt' | 'arangodb-root-password' | 'seed-sync-root' | 'fz_smtp.password' | 'fz_jetemail.apiKey' | 'fz_github.clientSecret' | 'fz_github.privateKey' | 'fz_github.webhookSecret' | 'CF_API_TOKEN' | 'CF_TUNNEL_TOKEN' | 'REALTIME_PUBLISH_SECRET' | 'REALTIME_TICKET_SECRET';
|
|
141
141
|
encryptedPath: string;
|
|
142
142
|
required: boolean;
|
|
143
143
|
}
|
|
@@ -987,6 +987,7 @@ function platformApiCredentialSpecs(options) {
|
|
|
987
987
|
];
|
|
988
988
|
return [
|
|
989
989
|
{ name: "arangodb-jwt", encryptedPath: "/etc/forgezero/creds/arangodb-jwt.cred", required: true },
|
|
990
|
+
{ name: "arangodb-root-password", encryptedPath: "/etc/forgezero/creds/arangodb-root-password.cred", required: true },
|
|
990
991
|
{ name: "seed-sync-root", encryptedPath: "/etc/forgezero/creds/seed-sync-root.cred", required: true },
|
|
991
992
|
...optional.filter(([, present]) => present).map(([name]) => ({
|
|
992
993
|
name,
|