@forgezero/agent 0.1.101 → 0.1.103
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.d.ts +6 -2
- package/dist/bootstrap.js +72 -6
- package/dist/fz-agent.js +1 -1
- package/dist/fz.js +172 -18
- package/dist/metal-bootstrap.d.ts +8 -0
- package/dist/metal-bootstrap.js +10 -3
- package/dist/operator-bootstrap.d.ts +10 -0
- package/dist/operator-bootstrap.js +127 -9
- package/dist/platform-bootstrap-runtime.d.ts +21 -1
- package/dist/platform-bootstrap-runtime.js +19 -1
- package/dist/platform-fleet-verification.js +21 -3
- package/dist/platform-genesis-config.d.ts +1 -1
- package/dist/platform-launch-profile.d.ts +5 -0
- package/dist/provision.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/agent-heartbeat.js
CHANGED
package/dist/bootstrap.d.ts
CHANGED
|
@@ -115,6 +115,10 @@ export interface PlatformBootstrapSecrets {
|
|
|
115
115
|
backupS3Secret?: string;
|
|
116
116
|
cloudflareTunnelToken?: string;
|
|
117
117
|
cloudflareApiToken?: string;
|
|
118
|
+
githubClientSecret?: string;
|
|
119
|
+
/** One-line base64 form accepted by prompts/env; decoded before systemd sealing. */
|
|
120
|
+
githubPrivateKeyBase64?: string;
|
|
121
|
+
githubWebhookSecret?: string;
|
|
118
122
|
}
|
|
119
123
|
export interface EnrolledComputeBootstrapSecrets {
|
|
120
124
|
enrolmentToken: string;
|
|
@@ -195,8 +199,8 @@ export declare const legacyBootstrapIdentityDigest: (config: BootstrapConfig) =>
|
|
|
195
199
|
/**
|
|
196
200
|
* Exact, reviewed identity migrations for an already-installed platform host.
|
|
197
201
|
* This is deliberately not a generic "stored coordinates match" escape hatch:
|
|
198
|
-
* only the development collector transition
|
|
199
|
-
*
|
|
202
|
+
* only the development collector transition, reviewed genesis evidence, and
|
|
203
|
+
* narrowly identified bootstrap-schema repairs are accepted.
|
|
200
204
|
*/
|
|
201
205
|
export declare function approvedPlatformRepairIdentityDigests(config: PlatformBootstrapConfig): readonly string[];
|
|
202
206
|
/** Exact prior-release identities accepted only while generation one is still unenrolled. */
|
package/dist/bootstrap.js
CHANGED
|
@@ -1386,7 +1386,7 @@ async function buildBootstrapBundle(input, exec = run) {
|
|
|
1386
1386
|
}
|
|
1387
1387
|
|
|
1388
1388
|
// src/bootstrap.ts
|
|
1389
|
-
import { createHash as createHash2, createHmac as createHmac2, randomBytes as randomBytes3 } from "crypto";
|
|
1389
|
+
import { createHash as createHash2, createHmac as createHmac2, createPrivateKey, randomBytes as randomBytes3 } from "crypto";
|
|
1390
1390
|
import {
|
|
1391
1391
|
chmodSync as chmodSync3,
|
|
1392
1392
|
existsSync as existsSync5,
|
|
@@ -1420,7 +1420,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
1420
1420
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
1421
1421
|
|
|
1422
1422
|
// src/version.ts
|
|
1423
|
-
var VERSION = "0.1.
|
|
1423
|
+
var VERSION = "0.1.103";
|
|
1424
1424
|
|
|
1425
1425
|
// src/software.ts
|
|
1426
1426
|
var PINNED_BUN_VERSION = "1.3.14";
|
|
@@ -3090,10 +3090,16 @@ var httpsOrigin = (name, raw) => {
|
|
|
3090
3090
|
};
|
|
3091
3091
|
var agentTelemetryOrigin = (raw) => raw === "http://127.0.0.1:4318" ? raw : httpsOrigin("agentOtlpEndpoint", raw);
|
|
3092
3092
|
function validatePlatformInitialInventory(value) {
|
|
3093
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["metalHostname", "region", "computes", "attestation", "deployment"].includes(key)) || !/^[A-Za-z0-9][A-Za-z0-9.-]{1,252}$/.test(value.metalHostname) || !value.region || typeof value.region !== "object" || Array.isArray(value.region) || Object.keys(value.region).some((key) => !["key", "label", "country", "city", "confidentialCapable"].includes(key)) || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(value.region.key) || !/^[A-Z]{2}$/.test(value.region.country) || value.region.confidentialCapable !== true || !Array.isArray(value.computes)) {
|
|
3093
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["metalHostname", "metalIdentity", "region", "computes", "attestation", "deployment"].includes(key)) || !/^[A-Za-z0-9][A-Za-z0-9.-]{1,252}$/.test(value.metalHostname) || !value.region || typeof value.region !== "object" || Array.isArray(value.region) || Object.keys(value.region).some((key) => !["key", "label", "country", "city", "confidentialCapable"].includes(key)) || !/^[a-z0-9][a-z0-9-]{0,62}$/.test(value.region.key) || !/^[A-Z]{2}$/.test(value.region.country) || value.region.confidentialCapable !== true || !Array.isArray(value.computes)) {
|
|
3094
3094
|
throw new Error("initial platform inventory coordinates are invalid");
|
|
3095
3095
|
}
|
|
3096
3096
|
safeAtom("initialInventory.region.label", value.region.label);
|
|
3097
|
+
if (value.metalIdentity !== undefined) {
|
|
3098
|
+
const identity = value.metalIdentity;
|
|
3099
|
+
if (!identity || typeof identity !== "object" || Array.isArray(identity) || Object.keys(identity).some((key) => !["nodeKey", "publicKeys"].includes(key)) || !/^[A-Za-z0-9_-]{43}$/.test(identity.nodeKey) || !identity.publicKeys || identity.publicKeys.ed25519 !== identity.nodeKey || !/^[A-Za-z0-9_-]{1,8192}$/.test(identity.publicKeys.mlDsa) || Object.keys(identity.publicKeys).some((key) => !["ed25519", "mlDsa"].includes(key))) {
|
|
3100
|
+
throw new Error("initial platform Metal identity is invalid");
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3097
3103
|
if (value.region.city !== undefined)
|
|
3098
3104
|
safeAtom("initialInventory.region.city", value.region.city);
|
|
3099
3105
|
if (value.deployment !== undefined && (!value.deployment || typeof value.deployment !== "object" || Array.isArray(value.deployment) || Object.keys(value.deployment).some((key) => !["source", "branch", "revision", "bundleSha256"].includes(key)) || value.deployment.source !== "bootstrap-bundle" || !["dev", "main"].includes(value.deployment.branch) || !/^[a-f0-9]{40}$/.test(value.deployment.revision) || !/^[a-f0-9]{64}$/.test(value.deployment.bundleSha256)))
|
|
@@ -3128,6 +3134,7 @@ function validatePlatformInitialInventory(value) {
|
|
|
3128
3134
|
}
|
|
3129
3135
|
return {
|
|
3130
3136
|
metalHostname: value.metalHostname,
|
|
3137
|
+
...value.metalIdentity ? { metalIdentity: structuredClone(value.metalIdentity) } : {},
|
|
3131
3138
|
region: { ...value.region },
|
|
3132
3139
|
computes: value.computes.map((compute, index) => ({
|
|
3133
3140
|
...computes[index],
|
|
@@ -3181,6 +3188,11 @@ function validatePlatformSharedEnvironment(input) {
|
|
|
3181
3188
|
} else if (input.email !== undefined) {
|
|
3182
3189
|
throw new Error("Bootstrap email provider must be smtp or jetemail.");
|
|
3183
3190
|
}
|
|
3191
|
+
if (input.githubApp) {
|
|
3192
|
+
if (!/^(?:Iv1\.[A-Fa-f0-9]{16}|Ov23li[A-Za-z0-9]{14,})$/.test(input.githubApp.clientId) || !/^[1-9][0-9]{0,19}$/.test(input.githubApp.appId) || !/^[a-z0-9][a-z0-9-]{0,99}$/.test(input.githubApp.slug)) {
|
|
3193
|
+
throw new Error("GitHub App client id, app id or slug is malformed.");
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3184
3196
|
boundedInteger("publicApiPort", input.publicApiPort, 1024, 65533);
|
|
3185
3197
|
if (!Array.isArray(input.seedSyncMembers) || input.seedSyncMembers.length < 3 || input.seedSyncMembers.length > 64 || new Set(input.seedSyncMembers).size !== input.seedSyncMembers.length) {
|
|
3186
3198
|
throw new Error("seedSyncMembers must contain 3 to 64 unique physical host identities.");
|
|
@@ -3311,6 +3323,9 @@ function renderPlatformSharedEnvironment(input) {
|
|
|
3311
3323
|
FZ_REALTIME_WORKER_SCRIPT: value.realtime?.workerScriptName ?? "",
|
|
3312
3324
|
FZ_REALTIME_ENDPOINT: value.realtime?.endpoint ?? "",
|
|
3313
3325
|
FZ_REALTIME_PRODUCER: value.realtime?.producer ?? "",
|
|
3326
|
+
FZ_GITHUB_CLIENT_ID: value.githubApp?.clientId ?? "",
|
|
3327
|
+
FZ_GITHUB_APP_ID: value.githubApp?.appId ?? "",
|
|
3328
|
+
FZ_GITHUB_APP_SLUG: value.githubApp?.slug ?? "",
|
|
3314
3329
|
FZ_PLATFORM_INITIAL_INVENTORY: value.initialInventory ? JSON.stringify(value.initialInventory) : ""
|
|
3315
3330
|
};
|
|
3316
3331
|
return `# Generated by fz bootstrap platform. Non-secret coordinates only.
|
|
@@ -3322,6 +3337,9 @@ function platformApiCredentialSpecs(options) {
|
|
|
3322
3337
|
const optional = [
|
|
3323
3338
|
["fz_smtp.password", options.emailProvider === "smtp"],
|
|
3324
3339
|
["fz_jetemail.apiKey", options.emailProvider === "jetemail"],
|
|
3340
|
+
["fz_github.clientSecret", options.githubApp],
|
|
3341
|
+
["fz_github.privateKey", options.githubApp],
|
|
3342
|
+
["fz_github.webhookSecret", options.githubApp],
|
|
3325
3343
|
["CF_API_TOKEN", options.cloudflareKv],
|
|
3326
3344
|
["CF_TUNNEL_TOKEN", options.cloudflareKv],
|
|
3327
3345
|
["REALTIME_PUBLISH_SECRET", options.realtime],
|
|
@@ -3717,7 +3735,17 @@ function validatePlatformBootstrapSecrets(config, input) {
|
|
|
3717
3735
|
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
3718
3736
|
throw new Error("bootstrap credential input must be an object");
|
|
3719
3737
|
const source = input;
|
|
3720
|
-
const allowed = [
|
|
3738
|
+
const allowed = [
|
|
3739
|
+
"clusterBootstrapCode",
|
|
3740
|
+
"emailSecret",
|
|
3741
|
+
"enrolmentToken",
|
|
3742
|
+
"backupS3Secret",
|
|
3743
|
+
"cloudflareTunnelToken",
|
|
3744
|
+
"cloudflareApiToken",
|
|
3745
|
+
"githubClientSecret",
|
|
3746
|
+
"githubPrivateKeyBase64",
|
|
3747
|
+
"githubWebhookSecret"
|
|
3748
|
+
];
|
|
3721
3749
|
const unknown = Object.keys(source).filter((key) => !allowed.includes(key));
|
|
3722
3750
|
if (unknown.length)
|
|
3723
3751
|
throw new Error(`bootstrap credential input contains unsupported field ${unknown[0]}`);
|
|
@@ -3727,6 +3755,9 @@ function validatePlatformBootstrapSecrets(config, input) {
|
|
|
3727
3755
|
const backupS3Secret = typeof source.backupS3Secret === "string" ? source.backupS3Secret.trim() : undefined;
|
|
3728
3756
|
const cloudflareTunnelToken = typeof source.cloudflareTunnelToken === "string" ? source.cloudflareTunnelToken.trim() : undefined;
|
|
3729
3757
|
const cloudflareApiToken = typeof source.cloudflareApiToken === "string" ? source.cloudflareApiToken.trim() : undefined;
|
|
3758
|
+
const githubClientSecret = typeof source.githubClientSecret === "string" ? source.githubClientSecret.trim() : undefined;
|
|
3759
|
+
const githubPrivateKeyBase64 = typeof source.githubPrivateKeyBase64 === "string" ? source.githubPrivateKeyBase64.trim() : undefined;
|
|
3760
|
+
const githubWebhookSecret = typeof source.githubWebhookSecret === "string" ? source.githubWebhookSecret.trim() : undefined;
|
|
3730
3761
|
if (!/^[a-f0-9]{64}$/i.test(clusterBootstrapCode))
|
|
3731
3762
|
throw new Error("cluster bootstrap code must contain exactly 64 hexadecimal characters");
|
|
3732
3763
|
if (!emailSecret || emailSecret.length > 16384 || /[\r\n\0]/.test(emailSecret))
|
|
@@ -3746,12 +3777,29 @@ function validatePlatformBootstrapSecrets(config, input) {
|
|
|
3746
3777
|
}
|
|
3747
3778
|
if (cloudflareTunnelToken)
|
|
3748
3779
|
validateCloudflareBootstrapSecretPair({ cloudflareTunnelToken, cloudflareApiToken });
|
|
3780
|
+
const githubConfigured = Boolean(config.runtime.environment.githubApp);
|
|
3781
|
+
if (githubConfigured !== Boolean(githubClientSecret && githubPrivateKeyBase64 && githubWebhookSecret)) {
|
|
3782
|
+
throw new Error("GitHub App coordinates require client secret, private key and webhook secret together");
|
|
3783
|
+
}
|
|
3784
|
+
if (githubConfigured) {
|
|
3785
|
+
if (githubClientSecret.length < 20 || githubClientSecret.length > 512 || /[\r\n\0]/.test(githubClientSecret) || githubWebhookSecret.length < 32 || githubWebhookSecret.length > 512 || /[\r\n\0]/.test(githubWebhookSecret)) {
|
|
3786
|
+
throw new Error("GitHub App client or webhook secret is malformed");
|
|
3787
|
+
}
|
|
3788
|
+
try {
|
|
3789
|
+
const pem = Buffer.from(githubPrivateKeyBase64, "base64").toString("utf8");
|
|
3790
|
+
if (createPrivateKey(pem).asymmetricKeyType !== "rsa")
|
|
3791
|
+
throw new Error("not RSA");
|
|
3792
|
+
} catch {
|
|
3793
|
+
throw new Error("GitHub App private key must be a base64-encoded RSA private key");
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3749
3796
|
return {
|
|
3750
3797
|
clusterBootstrapCode,
|
|
3751
3798
|
emailSecret,
|
|
3752
3799
|
...enrolmentToken ? { enrolmentToken } : {},
|
|
3753
3800
|
...backupS3Secret ? { backupS3Secret } : {},
|
|
3754
|
-
...cloudflareTunnelToken ? { cloudflareTunnelToken, cloudflareApiToken } : {}
|
|
3801
|
+
...cloudflareTunnelToken ? { cloudflareTunnelToken, cloudflareApiToken } : {},
|
|
3802
|
+
...githubConfigured ? { githubClientSecret, githubPrivateKeyBase64, githubWebhookSecret } : {}
|
|
3755
3803
|
};
|
|
3756
3804
|
}
|
|
3757
3805
|
var platformBootstrapRunner = (config) => config.kind === "platform" && config.database.role === "master";
|
|
@@ -4303,6 +4351,17 @@ function approvedPlatformRepairIdentityDigests(config) {
|
|
|
4303
4351
|
delete beforeAttestation.runtime.environment.initialInventory.attestation;
|
|
4304
4352
|
prior.push(beforeAttestation);
|
|
4305
4353
|
}
|
|
4354
|
+
const metalIdentity = config.runtime.environment.initialInventory?.metalIdentity;
|
|
4355
|
+
if (metalIdentity && config.enrolment.source === "genesis-derived") {
|
|
4356
|
+
const beforeMetalIdentity = structuredClone(config);
|
|
4357
|
+
delete beforeMetalIdentity.runtime.environment.initialInventory.metalIdentity;
|
|
4358
|
+
prior.push(beforeMetalIdentity);
|
|
4359
|
+
}
|
|
4360
|
+
if (config.database.role === "joiner" && config.enrolment.source === "genesis-derived" && config.runtime.environment.custodianEmail) {
|
|
4361
|
+
const beforeReplicaCustodianEmail = structuredClone(config);
|
|
4362
|
+
delete beforeReplicaCustodianEmail.runtime.environment.custodianEmail;
|
|
4363
|
+
prior.push(beforeReplicaCustodianEmail);
|
|
4364
|
+
}
|
|
4306
4365
|
const telemetryMigration = config.environment === "development" && config.telemetryEndpoint === LOCAL_DEBUG_OTEL_EXPORT && config.runtime.environment.agentOtlpEndpoint === "http://127.0.0.1:4318";
|
|
4307
4366
|
if (telemetryMigration) {
|
|
4308
4367
|
const beforeTelemetry = structuredClone(config);
|
|
@@ -4655,7 +4714,10 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
|
|
|
4655
4714
|
enrolmentToken: checked4.enrolmentToken,
|
|
4656
4715
|
backup: checked4.backupS3Secret,
|
|
4657
4716
|
cloudflareTunnelToken: checked4.cloudflareTunnelToken,
|
|
4658
|
-
cloudflareApiToken: checked4.cloudflareApiToken
|
|
4717
|
+
cloudflareApiToken: checked4.cloudflareApiToken,
|
|
4718
|
+
githubClientSecret: checked4.githubClientSecret,
|
|
4719
|
+
githubPrivateKey: checked4.githubPrivateKeyBase64 ? Buffer.from(checked4.githubPrivateKeyBase64, "base64").toString("utf8") : undefined,
|
|
4720
|
+
githubWebhookSecret: checked4.githubWebhookSecret
|
|
4659
4721
|
};
|
|
4660
4722
|
})() : undefined;
|
|
4661
4723
|
const enrolledPrivate = config.kind === "enrolled-compute" ? validateEnrolledComputeBootstrapSecrets(config, secrets) : undefined;
|
|
@@ -4753,6 +4815,9 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
|
|
|
4753
4815
|
const emailCredentialName = config.runtime.environment.email?.provider === "smtp" ? "fz_smtp.password" : config.runtime.environment.email?.provider === "jetemail" ? "fz_jetemail.apiKey" : undefined;
|
|
4754
4816
|
for (const [name, source] of Object.entries({
|
|
4755
4817
|
...emailCredentialName ? { [emailCredentialName]: platformPrivate.email } : {},
|
|
4818
|
+
"fz_github.clientSecret": platformPrivate.githubClientSecret,
|
|
4819
|
+
"fz_github.privateKey": platformPrivate.githubPrivateKey,
|
|
4820
|
+
"fz_github.webhookSecret": platformPrivate.githubWebhookSecret,
|
|
4756
4821
|
"backup.s3.secretAccessKey": platformPrivate.backup
|
|
4757
4822
|
})) {
|
|
4758
4823
|
if (source) {
|
|
@@ -4769,6 +4834,7 @@ async function applyBootstrap(input, host = localBootstrapHost(), secrets, depen
|
|
|
4769
4834
|
const credentials = platformApiCredentialSpecs({
|
|
4770
4835
|
emailProvider: runtime.environment.email?.provider,
|
|
4771
4836
|
cloudflareKv: cloudflareConfigured,
|
|
4837
|
+
githubApp: Boolean(runtime.environment.githubApp),
|
|
4772
4838
|
realtime: Boolean(runtime.environment.realtime)
|
|
4773
4839
|
});
|
|
4774
4840
|
const units = renderPlatformApiUnits({
|
package/dist/fz-agent.js
CHANGED