@forgezero/agent 0.1.35 → 0.1.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.d.ts +5 -0
- package/dist/bootstrap.js +60 -5
- package/dist/cli/agent-install.d.ts +3 -0
- package/dist/definition.js +9 -2
- package/dist/deploy-file.js +9 -2
- package/dist/fz-agent.js +492 -150
- package/dist/fz.js +69 -8
- package/dist/index.d.ts +2 -0
- package/dist/metal-bootstrap.js +1 -1
- package/dist/provision.d.ts +4 -0
- package/dist/provision.js +33 -5
- package/dist/software-helper.js +9 -2
- package/dist/software.d.ts +3 -1
- package/dist/software.js +12 -3
- package/dist/ssh-bootstrap.d.ts +72 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/schema/deploy-v2.json +1 -1
package/README.md
CHANGED
|
@@ -36,6 +36,35 @@ Install the service explicitly:
|
|
|
36
36
|
fz agent install --apply # writes a hardened systemd unit
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
## Host bootstrap
|
|
40
|
+
|
|
41
|
+
The package is also the host installer. There is no separate shell setup
|
|
42
|
+
script and no second implementation for tenant or platform machines:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
fz bootstrap platform # interactive plan, no mutation
|
|
46
|
+
sudo fz bootstrap platform --apply # initial DB+API or API-only host
|
|
47
|
+
sudo fz bootstrap tenant --bootstrap-config ./tenant.json --apply
|
|
48
|
+
sudo fz bootstrap metal --bootstrap-config ./metal.json --apply
|
|
49
|
+
sudo fz bootstrap status
|
|
50
|
+
sudo fz bootstrap repair --bootstrap-config ./original.json --apply
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Platform bootstrap supports an elastic Community ArangoDB 3.11.14 fleet. The
|
|
54
|
+
first three database-capable computes establish the writable cluster; later
|
|
55
|
+
database joiners and API-only computes use the same command with their typed
|
|
56
|
+
profile and an API-issued one-time enrolment file. Nothing treats three as a
|
|
57
|
+
maximum.
|
|
58
|
+
|
|
59
|
+
After genesis, a tenant can designate an enrolled compute as a bootstrap
|
|
60
|
+
runner. The API creates a project-bound, expiring job containing only public
|
|
61
|
+
SSH coordinates and a pinned Ed25519 host-key fingerprint. The runner claims it
|
|
62
|
+
with its hybrid node identity, reads its SSH key only from a local systemd
|
|
63
|
+
credential, pins one vetted DNS answer, transfers this packaged CLI, and runs
|
|
64
|
+
the same `fz bootstrap tenant` flow on the target. Claim renewal, completion,
|
|
65
|
+
retry and cancellation are fenced in the compute aggregate; the API never
|
|
66
|
+
stores the SSH private key or a plaintext enrolment token.
|
|
67
|
+
|
|
39
68
|
The attended Cloudflare resource phase is part of the same published command,
|
|
40
69
|
but remains separate from the root host install so its management token never
|
|
41
70
|
enters the API or Agent service environment:
|
package/dist/agent-heartbeat.js
CHANGED
package/dist/bootstrap.d.ts
CHANGED
|
@@ -68,6 +68,11 @@ export interface TenantBootstrapConfig {
|
|
|
68
68
|
deployRoot?: string;
|
|
69
69
|
software?: SoftwareRequirement[];
|
|
70
70
|
installCloudflared?: boolean;
|
|
71
|
+
/** Local-only runner authority. The API receives only the resulting node scope. */
|
|
72
|
+
bootstrapRunner?: {
|
|
73
|
+
sshPrivateKeyFile: string;
|
|
74
|
+
targetTelemetryEndpoint: string;
|
|
75
|
+
};
|
|
71
76
|
}
|
|
72
77
|
export type BootstrapConfig = PlatformBootstrapConfig | TenantBootstrapConfig;
|
|
73
78
|
export interface BootstrapStep {
|
package/dist/bootstrap.js
CHANGED
|
@@ -1214,7 +1214,7 @@ var UPDATE_RETRY_BASE_MS = 5 * 60000;
|
|
|
1214
1214
|
var UPDATE_RETRY_MAX_MS = 24 * 60 * 60000;
|
|
1215
1215
|
|
|
1216
1216
|
// src/version.ts
|
|
1217
|
-
var VERSION = "0.1.
|
|
1217
|
+
var VERSION = "0.1.36";
|
|
1218
1218
|
|
|
1219
1219
|
// src/software.ts
|
|
1220
1220
|
var BUN_INSTALLER_SHA256 = "bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd";
|
|
@@ -1245,6 +1245,11 @@ var UBUNTU_2604_X64 = [
|
|
|
1245
1245
|
requirement: { id: "ufw", version: "ubuntu-26.04" },
|
|
1246
1246
|
check: "command -v ufw >/dev/null",
|
|
1247
1247
|
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y ufw"
|
|
1248
|
+
},
|
|
1249
|
+
{
|
|
1250
|
+
requirement: { id: "openssh-client", version: "ubuntu-26.04" },
|
|
1251
|
+
check: "command -v ssh >/dev/null && command -v scp >/dev/null && command -v ssh-keyscan >/dev/null && command -v ssh-keygen >/dev/null",
|
|
1252
|
+
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y openssh-client"
|
|
1248
1253
|
}
|
|
1249
1254
|
];
|
|
1250
1255
|
|
|
@@ -1786,7 +1791,12 @@ function agentUnit(options) {
|
|
|
1786
1791
|
if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
|
|
1787
1792
|
throw new Error("migration pull and lifecycle profile must be supplied together");
|
|
1788
1793
|
}
|
|
1794
|
+
const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapTargetTelemetryEndpoint);
|
|
1795
|
+
if ([options.pullBootstrap, options.bootstrapSshCredentialPath, options.bootstrapTargetTelemetryEndpoint].some(Boolean) && !bootstrapEnabled) {
|
|
1796
|
+
throw new Error("bootstrap pull, SSH credential and target telemetry endpoint must be supplied together");
|
|
1797
|
+
}
|
|
1789
1798
|
const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
|
|
1799
|
+
const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
|
|
1790
1800
|
const warpValues = [
|
|
1791
1801
|
options.warpOrganization,
|
|
1792
1802
|
options.warpClientIdCredentialPath,
|
|
@@ -1852,6 +1862,9 @@ function agentUnit(options) {
|
|
|
1852
1862
|
options.publicApiUrl ? `FZ_PUBLIC_API_URL=${options.publicApiUrl}` : null,
|
|
1853
1863
|
options.pullDeployments ? "FZ_DEPLOY_PULL=true" : null,
|
|
1854
1864
|
options.pullMigrations ? "FZ_MIGRATION_PULL=true" : null,
|
|
1865
|
+
options.pullBootstrap ? "FZ_BOOTSTRAP_PULL=true" : null,
|
|
1866
|
+
options.pullBootstrap ? "FZ_BOOTSTRAP_SSH_KEY_CREDENTIAL=bootstrap-ssh-key" : null,
|
|
1867
|
+
options.pullBootstrap ? `FZ_BOOTSTRAP_TARGET_OTLP_ENDPOINT=${options.bootstrapTargetTelemetryEndpoint}` : null,
|
|
1855
1868
|
`FZ_AGENT_UPDATE_SOCKET=${DEFAULT_AGENT_UPDATE_SOCKET}`,
|
|
1856
1869
|
options.pullMigrations ? `FZ_LIFECYCLE_HELPER_SOCKET=${lifecycleHelperSocketPath}` : null
|
|
1857
1870
|
].filter((line) => line !== null);
|
|
@@ -1859,6 +1872,8 @@ function agentUnit(options) {
|
|
|
1859
1872
|
environment.push(`HOME=${deployRoot}/agent-home`, `XDG_CACHE_HOME=${deployRoot}/cache`);
|
|
1860
1873
|
}
|
|
1861
1874
|
const gitCredential = options.gitCredentialPath ? `LoadCredentialEncrypted=git-deploy-key:${options.gitCredentialPath}
|
|
1875
|
+
` : "";
|
|
1876
|
+
const bootstrapCredential = bootstrapEnabled ? `LoadCredentialEncrypted=bootstrap-ssh-key:${bootstrapSshCredentialPath}
|
|
1862
1877
|
` : "";
|
|
1863
1878
|
const projectCredentials = Object.entries(deploymentCredentials).map(([name, path]) => `LoadCredentialEncrypted=${name}:${path}`).join(`
|
|
1864
1879
|
`);
|
|
@@ -1913,7 +1928,7 @@ User=${user}
|
|
|
1913
1928
|
Group=${VAULT_GROUP}
|
|
1914
1929
|
${deploymentGroup}
|
|
1915
1930
|
LoadCredentialEncrypted=agent-seed:${seedCredentialPath}
|
|
1916
|
-
${gitCredential}${projectCredentials}${projectCredentials ? `
|
|
1931
|
+
${gitCredential}${bootstrapCredential}${projectCredentials}${projectCredentials ? `
|
|
1917
1932
|
` : ""}${snpPrepare}ExecStart=${bin}
|
|
1918
1933
|
Restart=always
|
|
1919
1934
|
RestartSec=2
|
|
@@ -1968,6 +1983,10 @@ function planProvision(options) {
|
|
|
1968
1983
|
if (Boolean(options.pullMigrations) !== Boolean(options.lifecycleProfilePath)) {
|
|
1969
1984
|
throw new Error("migration pull and lifecycle profile must be supplied together");
|
|
1970
1985
|
}
|
|
1986
|
+
const bootstrapEnabled = Boolean(options.pullBootstrap && options.bootstrapSshCredentialPath && options.bootstrapTargetTelemetryEndpoint);
|
|
1987
|
+
if ([options.pullBootstrap, options.bootstrapSshCredentialPath, options.bootstrapTargetTelemetryEndpoint].some(Boolean) && !bootstrapEnabled) {
|
|
1988
|
+
throw new Error("bootstrap pull, SSH credential and target telemetry endpoint must be supplied together");
|
|
1989
|
+
}
|
|
1971
1990
|
const warpValues = [
|
|
1972
1991
|
options.warpOrganization,
|
|
1973
1992
|
options.warpClientIdCredentialPath,
|
|
@@ -1993,13 +2012,20 @@ function planProvision(options) {
|
|
|
1993
2012
|
const gitPublicKeyDir = gitPublicKeyPath?.replace(/\/[^/]+$/, "");
|
|
1994
2013
|
const lifecycleProfilePath = lifecycleEnabled ? systemdPath(options.lifecycleProfilePath, "lifecycle profile") : undefined;
|
|
1995
2014
|
const lifecycleHelperSocketPath = lifecycleEnabled ? systemdPath(options.lifecycleHelperSocketPath ?? LIFECYCLE_HELPER_SOCKET, "lifecycle helper socket") : undefined;
|
|
2015
|
+
const bootstrapSshCredentialPath = bootstrapEnabled ? systemdPath(options.bootstrapSshCredentialPath, "bootstrap SSH credential") : undefined;
|
|
1996
2016
|
const warpClientIdCredentialPath = warpEnabled ? systemdPath(options.warpClientIdCredentialPath, "WARP client-id credential") : undefined;
|
|
1997
2017
|
const warpClientSecretCredentialPath = warpEnabled ? systemdPath(options.warpClientSecretCredentialPath, "WARP client-secret credential") : undefined;
|
|
1998
2018
|
return {
|
|
1999
2019
|
mode,
|
|
2000
2020
|
reason: reasonFor(mode),
|
|
2001
2021
|
unitPath: UNIT_PATH,
|
|
2002
|
-
unit: agentUnit({
|
|
2022
|
+
unit: agentUnit({
|
|
2023
|
+
...options,
|
|
2024
|
+
mode,
|
|
2025
|
+
lifecycleProfilePath,
|
|
2026
|
+
lifecycleHelperSocketPath,
|
|
2027
|
+
bootstrapSshCredentialPath
|
|
2028
|
+
}),
|
|
2003
2029
|
auxiliaryUnits: [
|
|
2004
2030
|
{ path: AGENT_SOCKET_UNIT_PATH, unit: agentSocketUnit(options) },
|
|
2005
2031
|
{ path: AGENT_SOCKET_PROXY_UNIT_PATH, unit: agentSocketProxyUnit(options) },
|
|
@@ -2600,6 +2626,7 @@ var SEED_CREDENTIAL = `${CREDS}/seed-sync-root.cred`;
|
|
|
2600
2626
|
var BACKUP_RECOVERY_CREDENTIAL = `${CREDS}/backup-recovery-root.cred`;
|
|
2601
2627
|
var WARP_CLIENT_ID_CREDENTIAL = `${CREDS}/warp-auth-client-id.cred`;
|
|
2602
2628
|
var WARP_CLIENT_SECRET_CREDENTIAL = `${CREDS}/warp-auth-client-secret.cred`;
|
|
2629
|
+
var BOOTSTRAP_SSH_CREDENTIAL = `${CREDS}/bootstrap-ssh-key.cred`;
|
|
2603
2630
|
var PLATFORM_ENROL_SOURCE = "/run/forgezero-platform-enrol-token";
|
|
2604
2631
|
var DB_MODE_EVIDENCE = "/var/lib/forgezero-cluster/server-mode.json";
|
|
2605
2632
|
var LIFECYCLE_PROFILE = "/etc/forgezero/lifecycle.json";
|
|
@@ -2662,6 +2689,20 @@ function validateBootstrapConfig(value) {
|
|
|
2662
2689
|
}
|
|
2663
2690
|
if (!value.enrolTokenFile)
|
|
2664
2691
|
throw new Error("tenant bootstrap requires --enrol-token-file");
|
|
2692
|
+
if (value.bootstrapRunner) {
|
|
2693
|
+
if (!value.bootstrapRunner.sshPrivateKeyFile.startsWith("/") || /[\r\n]/.test(value.bootstrapRunner.sshPrivateKeyFile)) {
|
|
2694
|
+
throw new Error("bootstrap runner SSH private-key file must be absolute");
|
|
2695
|
+
}
|
|
2696
|
+
let targetTelemetry;
|
|
2697
|
+
try {
|
|
2698
|
+
targetTelemetry = new URL(value.bootstrapRunner.targetTelemetryEndpoint);
|
|
2699
|
+
} catch {
|
|
2700
|
+
throw new Error("bootstrap target telemetry endpoint is malformed");
|
|
2701
|
+
}
|
|
2702
|
+
if (targetTelemetry.protocol !== "https:" || targetTelemetry.username || targetTelemetry.password || targetTelemetry.search || targetTelemetry.hash || targetTelemetry.port && targetTelemetry.port !== "443") {
|
|
2703
|
+
throw new Error("bootstrap target telemetry must be public HTTPS on port 443");
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2665
2706
|
return value;
|
|
2666
2707
|
}
|
|
2667
2708
|
if (!PLATFORM_BOOTSTRAP_PROFILES.includes(value.profile))
|
|
@@ -2723,7 +2764,11 @@ function validateBootstrapConfig(value) {
|
|
|
2723
2764
|
}
|
|
2724
2765
|
function planBootstrap(input, initialized = false) {
|
|
2725
2766
|
const config = validateBootstrapConfig(structuredClone(input));
|
|
2726
|
-
const software = config.kind === "tenant" ? [
|
|
2767
|
+
const software = config.kind === "tenant" ? [
|
|
2768
|
+
...config.software ?? [],
|
|
2769
|
+
...config.bootstrapRunner && !config.software?.some(({ id }) => id === "openssh-client") ? [{ id: "openssh-client", version: "ubuntu-26.04" }] : [],
|
|
2770
|
+
...config.installCloudflared ? [{ id: "cloudflared", version: "2026.7.3" }] : []
|
|
2771
|
+
] : [
|
|
2727
2772
|
...config.firewall.enabled ? [{ id: "ufw", version: "ubuntu-26.04" }] : [],
|
|
2728
2773
|
{ id: "bun", version: "1.3.14" },
|
|
2729
2774
|
{ id: "nginx", version: "ubuntu-26.04" },
|
|
@@ -2958,6 +3003,10 @@ async function applyBootstrap(input, host = localBootstrapHost()) {
|
|
|
2958
3003
|
const alreadyEnrolled = host.exists("/var/lib/forgezero/enrolment.json");
|
|
2959
3004
|
host.mkdir(CREDS, 448);
|
|
2960
3005
|
host.mkdir("/var/lib/forgezero", 448);
|
|
3006
|
+
if (config.kind === "tenant" && config.bootstrapRunner && !host.exists(BOOTSTRAP_SSH_CREDENTIAL)) {
|
|
3007
|
+
await seal(host, "bootstrap-ssh-key", BOOTSTRAP_SSH_CREDENTIAL, privateFile(host, config.bootstrapRunner.sshPrivateKeyFile, "bootstrap runner SSH private key"));
|
|
3008
|
+
host.remove(config.bootstrapRunner.sshPrivateKeyFile);
|
|
3009
|
+
}
|
|
2961
3010
|
if (cloudflare?.warp) {
|
|
2962
3011
|
await seal(host, "warp-auth-client-id", WARP_CLIENT_ID_CREDENTIAL, cloudflare.warp.clientId);
|
|
2963
3012
|
await seal(host, "warp-auth-client-secret", WARP_CLIENT_SECRET_CREDENTIAL, cloudflare.warp.clientSecret);
|
|
@@ -3157,7 +3206,8 @@ function strictBootstrapDocument(value) {
|
|
|
3157
3206
|
"cloudflareHandoff",
|
|
3158
3207
|
"realm",
|
|
3159
3208
|
"enrolTokenFile",
|
|
3160
|
-
"software"
|
|
3209
|
+
"software",
|
|
3210
|
+
"bootstrapRunner"
|
|
3161
3211
|
], "bootstrap config");
|
|
3162
3212
|
if (root.kind === "platform") {
|
|
3163
3213
|
exactKeys(root.firewall, ["enabled", "sshPort", "privateCidrs"], "firewall config");
|
|
@@ -3222,6 +3272,8 @@ function strictBootstrapDocument(value) {
|
|
|
3222
3272
|
exactKeys(environment.cloudflare.warp, ["organization", "virtualNetworkId", "deviceProfileId"], "Cloudflare WARP runtime config");
|
|
3223
3273
|
}
|
|
3224
3274
|
} else if (root.kind === "tenant") {
|
|
3275
|
+
if (root.bootstrapRunner !== undefined)
|
|
3276
|
+
exactKeys(root.bootstrapRunner, ["sshPrivateKeyFile", "targetTelemetryEndpoint"], "bootstrap runner config");
|
|
3225
3277
|
for (const key of ["environment", "profile", "computeReference", "database", "platformEnrolTokenFile", "runtime", "cloudflareHandoff"]) {
|
|
3226
3278
|
if (root[key] !== undefined && key !== "profile")
|
|
3227
3279
|
throw new Error(`tenant bootstrap cannot contain ${key}`);
|
|
@@ -3316,6 +3368,9 @@ function localBootstrapHost() {
|
|
|
3316
3368
|
generateGitIdentity: true,
|
|
3317
3369
|
pullDeployments: true,
|
|
3318
3370
|
pullMigrations: config.kind === "platform",
|
|
3371
|
+
pullBootstrap: config.kind === "tenant" && Boolean(config.bootstrapRunner),
|
|
3372
|
+
bootstrapSshCredentialPath: config.kind === "tenant" && config.bootstrapRunner ? BOOTSTRAP_SSH_CREDENTIAL : undefined,
|
|
3373
|
+
bootstrapTargetTelemetryEndpoint: config.kind === "tenant" ? config.bootstrapRunner?.targetTelemetryEndpoint : undefined,
|
|
3319
3374
|
lifecycleProfilePath: config.kind === "platform" ? LIFECYCLE_PROFILE : undefined,
|
|
3320
3375
|
...config.kind === "platform" && config.runtime.environment.cloudflare?.warp ? {
|
|
3321
3376
|
warpOrganization: config.runtime.environment.cloudflare.warp.organization,
|
|
@@ -56,6 +56,9 @@ export interface InstallOptions {
|
|
|
56
56
|
runnerLoopbackPorts?: readonly number[];
|
|
57
57
|
runnerPublicTcpPorts?: readonly number[];
|
|
58
58
|
pullMigrations?: boolean;
|
|
59
|
+
pullBootstrap?: boolean;
|
|
60
|
+
bootstrapSshCredentialPath?: string;
|
|
61
|
+
bootstrapTargetTelemetryEndpoint?: string;
|
|
59
62
|
lifecycleProfilePath?: string;
|
|
60
63
|
lifecycleHelperSocketPath?: string;
|
|
61
64
|
warpOrganization?: string;
|
package/dist/definition.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/software.ts
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
var PINNED_BUN_VERSION = "1.3.14";
|
|
3
4
|
var BUN_INSTALLER_SHA256 = "bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd";
|
|
4
5
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
5
6
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
@@ -11,7 +12,8 @@ var SOFTWARE_CATALOG = [
|
|
|
11
12
|
{ id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
12
13
|
{ id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
13
14
|
{ id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
14
|
-
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
15
|
+
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
16
|
+
{ id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
15
17
|
];
|
|
16
18
|
var UBUNTU_2604_X64 = [
|
|
17
19
|
{
|
|
@@ -38,6 +40,11 @@ var UBUNTU_2604_X64 = [
|
|
|
38
40
|
requirement: { id: "ufw", version: "ubuntu-26.04" },
|
|
39
41
|
check: "command -v ufw >/dev/null",
|
|
40
42
|
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y ufw"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
requirement: { id: "openssh-client", version: "ubuntu-26.04" },
|
|
46
|
+
check: "command -v ssh >/dev/null && command -v scp >/dev/null && command -v ssh-keyscan >/dev/null && command -v ssh-keygen >/dev/null",
|
|
47
|
+
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y openssh-client"
|
|
41
48
|
}
|
|
42
49
|
];
|
|
43
50
|
function observeSoftwareHost(osRelease = readFileSync("/etc/os-release", "utf8"), architecture = process.arch) {
|
|
@@ -62,7 +69,7 @@ function validateSoftwareRequirements(value, _options = {}) {
|
|
|
62
69
|
if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
|
|
63
70
|
throw new Error("software requirement contains an unknown field");
|
|
64
71
|
}
|
|
65
|
-
if (!["bun", "nginx", "arangodb", "cloudflared", "ufw"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
72
|
+
if (!["bun", "nginx", "arangodb", "cloudflared", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
66
73
|
throw new Error("software requirement coordinate is invalid");
|
|
67
74
|
}
|
|
68
75
|
const requirement = { id: row.id, version: row.version };
|
package/dist/deploy-file.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/software.ts
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
var PINNED_BUN_VERSION = "1.3.14";
|
|
3
4
|
var BUN_INSTALLER_SHA256 = "bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd";
|
|
4
5
|
var ARANGO_SHA256 = "b5a9197b4343f2ed554e1ebc1ef8e6529c7c39cde0035cdc311a4747a3355066";
|
|
5
6
|
var CLOUDFLARED_SHA256 = "9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17";
|
|
@@ -11,7 +12,8 @@ var SOFTWARE_CATALOG = [
|
|
|
11
12
|
{ id: "nginx", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
12
13
|
{ id: "arangodb", version: "3.11.14", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
13
14
|
{ id: "cloudflared", version: "2026.7.3", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
14
|
-
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
15
|
+
{ id: "ufw", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" },
|
|
16
|
+
{ id: "openssh-client", version: "ubuntu-26.04", status: "active", os: "ubuntu", osVersion: "26.04", architecture: "x64", evidence: "reviewed-strategy-and-tests" }
|
|
15
17
|
];
|
|
16
18
|
var UBUNTU_2604_X64 = [
|
|
17
19
|
{
|
|
@@ -38,6 +40,11 @@ var UBUNTU_2604_X64 = [
|
|
|
38
40
|
requirement: { id: "ufw", version: "ubuntu-26.04" },
|
|
39
41
|
check: "command -v ufw >/dev/null",
|
|
40
42
|
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y ufw"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
requirement: { id: "openssh-client", version: "ubuntu-26.04" },
|
|
46
|
+
check: "command -v ssh >/dev/null && command -v scp >/dev/null && command -v ssh-keyscan >/dev/null && command -v ssh-keygen >/dev/null",
|
|
47
|
+
install: "DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y openssh-client"
|
|
41
48
|
}
|
|
42
49
|
];
|
|
43
50
|
function observeSoftwareHost(osRelease = readFileSync("/etc/os-release", "utf8"), architecture = process.arch) {
|
|
@@ -62,7 +69,7 @@ function validateSoftwareRequirements(value, _options = {}) {
|
|
|
62
69
|
if (Object.keys(row).some((key) => key !== "id" && key !== "version")) {
|
|
63
70
|
throw new Error("software requirement contains an unknown field");
|
|
64
71
|
}
|
|
65
|
-
if (!["bun", "nginx", "arangodb", "cloudflared", "ufw"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
72
|
+
if (!["bun", "nginx", "arangodb", "cloudflared", "ufw", "openssh-client"].includes(String(row.id)) || typeof row.version !== "string" || !/^[A-Za-z0-9][A-Za-z0-9.-]{0,31}$/.test(row.version)) {
|
|
66
73
|
throw new Error("software requirement coordinate is invalid");
|
|
67
74
|
}
|
|
68
75
|
const requirement = { id: row.id, version: row.version };
|