@forgezero/agent 0.1.23 → 0.1.24
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/cli/agent-install.d.ts +4 -0
- package/dist/fz-agent.js +26 -3
- package/dist/fz.js +31 -2
- package/dist/guest-enrolment.d.ts +9 -0
- package/dist/guest-enrolment.js +22 -0
- package/dist/provision.d.ts +5 -0
- package/dist/provision.js +26 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -56,6 +56,10 @@ export interface InstallOptions {
|
|
|
56
56
|
warpOrganization?: string;
|
|
57
57
|
warpClientIdCredentialPath?: string;
|
|
58
58
|
warpClientSecretCredentialPath?: string;
|
|
59
|
+
cloudflareAccountId?: string;
|
|
60
|
+
cloudflareTunnelId?: string;
|
|
61
|
+
cloudflareVirtualNetworkId?: string;
|
|
62
|
+
cloudflareWarpPolicyId?: string;
|
|
59
63
|
binPath?: string;
|
|
60
64
|
sourceBinPath?: string;
|
|
61
65
|
user?: string;
|
package/dist/fz-agent.js
CHANGED
|
@@ -1158,6 +1158,26 @@ import {
|
|
|
1158
1158
|
writeFileSync as writeFileSync2
|
|
1159
1159
|
} from "fs";
|
|
1160
1160
|
import { dirname as dirname2 } from "path";
|
|
1161
|
+
function privateNetworkAttachmentFromEnvironment(env = process.env) {
|
|
1162
|
+
const values = {
|
|
1163
|
+
accountId: env.FZ_CF_ACCOUNT_ID?.trim() ?? "",
|
|
1164
|
+
tunnelId: env.FZ_CF_TUNNEL_ID?.trim() ?? "",
|
|
1165
|
+
virtualNetworkId: env.FZ_CF_VIRTUAL_NETWORK_ID?.trim() ?? "",
|
|
1166
|
+
warpPolicyId: env.FZ_CF_WARP_POLICY_ID?.trim() ?? ""
|
|
1167
|
+
};
|
|
1168
|
+
const supplied = [values.accountId, values.tunnelId, values.virtualNetworkId, values.warpPolicyId].some(Boolean);
|
|
1169
|
+
if (!supplied)
|
|
1170
|
+
return;
|
|
1171
|
+
if (!values.accountId || !values.tunnelId || !values.warpPolicyId) {
|
|
1172
|
+
throw new Error("private-network attachment requires account, Tunnel and WARP policy ids");
|
|
1173
|
+
}
|
|
1174
|
+
return {
|
|
1175
|
+
accountId: values.accountId,
|
|
1176
|
+
tunnelId: values.tunnelId,
|
|
1177
|
+
...values.virtualNetworkId ? { virtualNetworkId: values.virtualNetworkId } : {},
|
|
1178
|
+
warpPolicyId: values.warpPolicyId
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1161
1181
|
var validBinding = (value, expectedNodeKey) => {
|
|
1162
1182
|
if (!value || typeof value !== "object")
|
|
1163
1183
|
return false;
|
|
@@ -1204,6 +1224,7 @@ async function enrolGuestIdentity(options) {
|
|
|
1204
1224
|
token,
|
|
1205
1225
|
label: options.label,
|
|
1206
1226
|
gitDeployPublicKey: options.gitDeployPublicKey,
|
|
1227
|
+
privateNetworkAttachment: options.privateNetworkAttachment,
|
|
1207
1228
|
publicKeys: {
|
|
1208
1229
|
ed25519: options.keys.ed25519.publicKey,
|
|
1209
1230
|
mlDsa: options.keys.mlDsa.publicKey
|
|
@@ -2919,7 +2940,7 @@ function materializeWarpMdm(options) {
|
|
|
2919
2940
|
}
|
|
2920
2941
|
|
|
2921
2942
|
// src/version.ts
|
|
2922
|
-
var VERSION = "0.1.
|
|
2943
|
+
var VERSION = "0.1.24";
|
|
2923
2944
|
|
|
2924
2945
|
// src/index.ts
|
|
2925
2946
|
function loadOrCreateSeed(path) {
|
|
@@ -3062,7 +3083,8 @@ if (import.meta.main) {
|
|
|
3062
3083
|
nodeKey: nodeKey2,
|
|
3063
3084
|
keys: keys2,
|
|
3064
3085
|
label: process.env.FZ_NODE_LABEL,
|
|
3065
|
-
gitDeployPublicKey: process.env.FZ_GIT_PUBLIC_KEY_FILE ? readFileSync5(process.env.FZ_GIT_PUBLIC_KEY_FILE, "utf8").trim() : undefined
|
|
3086
|
+
gitDeployPublicKey: process.env.FZ_GIT_PUBLIC_KEY_FILE ? readFileSync5(process.env.FZ_GIT_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
|
|
3087
|
+
privateNetworkAttachment: privateNetworkAttachmentFromEnvironment()
|
|
3066
3088
|
});
|
|
3067
3089
|
console.log(`[agent] enrolled ${binding2.computeReference} in project ${binding2.projectKey}/${binding2.environmentKey}`);
|
|
3068
3090
|
process.exit(0);
|
|
@@ -3280,7 +3302,8 @@ if (import.meta.main) {
|
|
|
3280
3302
|
nodeKey,
|
|
3281
3303
|
keys,
|
|
3282
3304
|
label: process.env.FZ_NODE_LABEL,
|
|
3283
|
-
gitDeployPublicKey: process.env.FZ_GIT_PUBLIC_KEY_FILE ? readFileSync5(process.env.FZ_GIT_PUBLIC_KEY_FILE, "utf8").trim() : undefined
|
|
3305
|
+
gitDeployPublicKey: process.env.FZ_GIT_PUBLIC_KEY_FILE ? readFileSync5(process.env.FZ_GIT_PUBLIC_KEY_FILE, "utf8").trim() : undefined,
|
|
3306
|
+
privateNetworkAttachment: privateNetworkAttachmentFromEnvironment()
|
|
3284
3307
|
});
|
|
3285
3308
|
console.log(`[agent] enrolled ${binding.computeReference} in project ${binding.projectKey}/${binding.environmentKey}`);
|
|
3286
3309
|
}
|
package/dist/fz.js
CHANGED
|
@@ -259,6 +259,16 @@ function agentEnrolmentUnit(options) {
|
|
|
259
259
|
` : "";
|
|
260
260
|
const gitPublicKey = options.gitPublicKeyPath ? `Environment=FZ_GIT_PUBLIC_KEY_FILE=${options.gitPublicKeyPath}
|
|
261
261
|
` : "";
|
|
262
|
+
const networkAttachment = [
|
|
263
|
+
options.cloudflareAccountId ? `Environment=FZ_CF_ACCOUNT_ID=${options.cloudflareAccountId}
|
|
264
|
+
` : "",
|
|
265
|
+
options.cloudflareTunnelId ? `Environment=FZ_CF_TUNNEL_ID=${options.cloudflareTunnelId}
|
|
266
|
+
` : "",
|
|
267
|
+
options.cloudflareVirtualNetworkId ? `Environment=FZ_CF_VIRTUAL_NETWORK_ID=${options.cloudflareVirtualNetworkId}
|
|
268
|
+
` : "",
|
|
269
|
+
options.cloudflareWarpPolicyId ? `Environment=FZ_CF_WARP_POLICY_ID=${options.cloudflareWarpPolicyId}
|
|
270
|
+
` : ""
|
|
271
|
+
].join("");
|
|
262
272
|
const stateDir = options.enrolStatePath.replace(/\/[^/]+$/, "");
|
|
263
273
|
return `[Unit]
|
|
264
274
|
Description=Bind this machine to its ForgeZero compute
|
|
@@ -277,7 +287,7 @@ Environment=FZ_SEED_CREDENTIAL=agent-seed
|
|
|
277
287
|
Environment=FZ_ENROL_TOKEN_CREDENTIAL=enrol-token
|
|
278
288
|
Environment=FZ_ENROL_STATE_FILE=${options.enrolStatePath}
|
|
279
289
|
Environment=FZ_API=${options.apiUrl}
|
|
280
|
-
${label}${gitPublicKey}ExecStart=${bin} enrol
|
|
290
|
+
${label}${gitPublicKey}${networkAttachment}ExecStart=${bin} enrol
|
|
281
291
|
# A '+' fixed command runs as root solely to remove the host-bound one-time
|
|
282
292
|
# ciphertext. Tenant code and the agent never receive a privilege boundary.
|
|
283
293
|
ExecStartPost=+/usr/bin/rm -f ${options.enrolTokenCredentialPath}
|
|
@@ -355,6 +365,21 @@ function agentUnit(options) {
|
|
|
355
365
|
const warpEnabled = warpValues.every(Boolean);
|
|
356
366
|
if (warpValues.some(Boolean) && !warpEnabled)
|
|
357
367
|
throw new Error("WARP configuration must be supplied together");
|
|
368
|
+
const networkAttachmentValues = [
|
|
369
|
+
options.cloudflareAccountId,
|
|
370
|
+
options.cloudflareTunnelId,
|
|
371
|
+
options.cloudflareVirtualNetworkId,
|
|
372
|
+
options.cloudflareWarpPolicyId
|
|
373
|
+
];
|
|
374
|
+
if (networkAttachmentValues.some(Boolean)) {
|
|
375
|
+
if (!options.cloudflareAccountId || !options.cloudflareTunnelId || !options.cloudflareWarpPolicyId) {
|
|
376
|
+
throw new Error("private-network attachment requires account, Tunnel and WARP policy ids");
|
|
377
|
+
}
|
|
378
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
379
|
+
if (!/^[a-f0-9]{32}$/i.test(options.cloudflareAccountId) || !uuid.test(options.cloudflareTunnelId) || options.cloudflareVirtualNetworkId && !uuid.test(options.cloudflareVirtualNetworkId) || !/^[A-Za-z0-9-]{1,64}$/.test(options.cloudflareWarpPolicyId)) {
|
|
380
|
+
throw new Error("private-network attachment coordinates are invalid");
|
|
381
|
+
}
|
|
382
|
+
}
|
|
358
383
|
const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath && options.enrolStatePath);
|
|
359
384
|
const deploymentEnvironment = options.deploymentEnvironment ?? {};
|
|
360
385
|
const deploymentCredentials = options.deploymentCredentials ?? {};
|
|
@@ -941,7 +966,7 @@ async function resolveIdentity(selector, socketPath) {
|
|
|
941
966
|
}
|
|
942
967
|
|
|
943
968
|
// src/version.ts
|
|
944
|
-
var VERSION = "0.1.
|
|
969
|
+
var VERSION = "0.1.24";
|
|
945
970
|
|
|
946
971
|
// src/cli/index.ts
|
|
947
972
|
var DEFAULT_MODE = THRESHOLD_MODES[0].id;
|
|
@@ -1170,6 +1195,10 @@ async function cmdAgent(options, args) {
|
|
|
1170
1195
|
warpOrganization: process.env.FZ_WARP_ORGANIZATION,
|
|
1171
1196
|
warpClientIdCredentialPath: process.env.FZ_WARP_CLIENT_ID_CREDENTIAL_PATH,
|
|
1172
1197
|
warpClientSecretCredentialPath: process.env.FZ_WARP_CLIENT_SECRET_CREDENTIAL_PATH,
|
|
1198
|
+
cloudflareAccountId: process.env.FZ_CF_ACCOUNT_ID,
|
|
1199
|
+
cloudflareTunnelId: process.env.FZ_CF_TUNNEL_ID,
|
|
1200
|
+
cloudflareVirtualNetworkId: process.env.FZ_CF_VIRTUAL_NETWORK_ID,
|
|
1201
|
+
cloudflareWarpPolicyId: process.env.FZ_CF_WARP_POLICY_ID,
|
|
1173
1202
|
...options.enrol ? {
|
|
1174
1203
|
pullDeployments: true,
|
|
1175
1204
|
enrolTokenSourcePath,
|
|
@@ -7,6 +7,14 @@ export interface GuestBinding {
|
|
|
7
7
|
realm: 'platform' | 'tenant';
|
|
8
8
|
tenantSlug?: string;
|
|
9
9
|
}
|
|
10
|
+
export interface GuestPrivateNetworkAttachment {
|
|
11
|
+
accountId: string;
|
|
12
|
+
tunnelId: string;
|
|
13
|
+
virtualNetworkId?: string;
|
|
14
|
+
warpPolicyId: string;
|
|
15
|
+
}
|
|
16
|
+
/** Fail closed when setup supplied only part of a cross-network attachment. */
|
|
17
|
+
export declare function privateNetworkAttachmentFromEnvironment(env?: Record<string, string | undefined>): GuestPrivateNetworkAttachment | undefined;
|
|
10
18
|
export interface GuestEnrolmentOptions {
|
|
11
19
|
apiUrl: string;
|
|
12
20
|
/** Already-unsealed systemd credential. Production uses this path. */
|
|
@@ -21,6 +29,7 @@ export interface GuestEnrolmentOptions {
|
|
|
21
29
|
keys: NodeKeyPair;
|
|
22
30
|
label?: string;
|
|
23
31
|
gitDeployPublicKey?: string;
|
|
32
|
+
privateNetworkAttachment?: GuestPrivateNetworkAttachment;
|
|
24
33
|
fetch?: (input: URL, init: RequestInit) => Promise<Response>;
|
|
25
34
|
requestTimeoutMs?: number;
|
|
26
35
|
}
|
package/dist/guest-enrolment.js
CHANGED
|
@@ -68,6 +68,26 @@ async function postSignedNode(options, path, body) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// src/guest-enrolment.ts
|
|
71
|
+
function privateNetworkAttachmentFromEnvironment(env = process.env) {
|
|
72
|
+
const values = {
|
|
73
|
+
accountId: env.FZ_CF_ACCOUNT_ID?.trim() ?? "",
|
|
74
|
+
tunnelId: env.FZ_CF_TUNNEL_ID?.trim() ?? "",
|
|
75
|
+
virtualNetworkId: env.FZ_CF_VIRTUAL_NETWORK_ID?.trim() ?? "",
|
|
76
|
+
warpPolicyId: env.FZ_CF_WARP_POLICY_ID?.trim() ?? ""
|
|
77
|
+
};
|
|
78
|
+
const supplied = [values.accountId, values.tunnelId, values.virtualNetworkId, values.warpPolicyId].some(Boolean);
|
|
79
|
+
if (!supplied)
|
|
80
|
+
return;
|
|
81
|
+
if (!values.accountId || !values.tunnelId || !values.warpPolicyId) {
|
|
82
|
+
throw new Error("private-network attachment requires account, Tunnel and WARP policy ids");
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
accountId: values.accountId,
|
|
86
|
+
tunnelId: values.tunnelId,
|
|
87
|
+
...values.virtualNetworkId ? { virtualNetworkId: values.virtualNetworkId } : {},
|
|
88
|
+
warpPolicyId: values.warpPolicyId
|
|
89
|
+
};
|
|
90
|
+
}
|
|
71
91
|
var validBinding = (value, expectedNodeKey) => {
|
|
72
92
|
if (!value || typeof value !== "object")
|
|
73
93
|
return false;
|
|
@@ -114,6 +134,7 @@ async function enrolGuestIdentity(options) {
|
|
|
114
134
|
token,
|
|
115
135
|
label: options.label,
|
|
116
136
|
gitDeployPublicKey: options.gitDeployPublicKey,
|
|
137
|
+
privateNetworkAttachment: options.privateNetworkAttachment,
|
|
117
138
|
publicKeys: {
|
|
118
139
|
ed25519: options.keys.ed25519.publicKey,
|
|
119
140
|
mlDsa: options.keys.mlDsa.publicKey
|
|
@@ -138,6 +159,7 @@ async function enrolGuestIdentity(options) {
|
|
|
138
159
|
return binding;
|
|
139
160
|
}
|
|
140
161
|
export {
|
|
162
|
+
privateNetworkAttachmentFromEnvironment,
|
|
141
163
|
loadGuestBinding,
|
|
142
164
|
enrolGuestIdentity
|
|
143
165
|
};
|
package/dist/provision.d.ts
CHANGED
|
@@ -101,6 +101,11 @@ export interface UnitOptions {
|
|
|
101
101
|
warpOrganization?: string;
|
|
102
102
|
warpClientIdCredentialPath?: string;
|
|
103
103
|
warpClientSecretCredentialPath?: string;
|
|
104
|
+
/** Non-secret coordinates bound through the PQ-signed enrolment request. */
|
|
105
|
+
cloudflareAccountId?: string;
|
|
106
|
+
cloudflareTunnelId?: string;
|
|
107
|
+
cloudflareVirtualNetworkId?: string;
|
|
108
|
+
cloudflareWarpPolicyId?: string;
|
|
104
109
|
/** Where `fz-agent` ended up. `bun add -g` puts it on PATH. */
|
|
105
110
|
binPath?: string;
|
|
106
111
|
/** Package-owned binary copied to binPath before hardened units start. */
|
package/dist/provision.js
CHANGED
|
@@ -151,6 +151,16 @@ function agentEnrolmentUnit(options) {
|
|
|
151
151
|
` : "";
|
|
152
152
|
const gitPublicKey = options.gitPublicKeyPath ? `Environment=FZ_GIT_PUBLIC_KEY_FILE=${options.gitPublicKeyPath}
|
|
153
153
|
` : "";
|
|
154
|
+
const networkAttachment = [
|
|
155
|
+
options.cloudflareAccountId ? `Environment=FZ_CF_ACCOUNT_ID=${options.cloudflareAccountId}
|
|
156
|
+
` : "",
|
|
157
|
+
options.cloudflareTunnelId ? `Environment=FZ_CF_TUNNEL_ID=${options.cloudflareTunnelId}
|
|
158
|
+
` : "",
|
|
159
|
+
options.cloudflareVirtualNetworkId ? `Environment=FZ_CF_VIRTUAL_NETWORK_ID=${options.cloudflareVirtualNetworkId}
|
|
160
|
+
` : "",
|
|
161
|
+
options.cloudflareWarpPolicyId ? `Environment=FZ_CF_WARP_POLICY_ID=${options.cloudflareWarpPolicyId}
|
|
162
|
+
` : ""
|
|
163
|
+
].join("");
|
|
154
164
|
const stateDir = options.enrolStatePath.replace(/\/[^/]+$/, "");
|
|
155
165
|
return `[Unit]
|
|
156
166
|
Description=Bind this machine to its ForgeZero compute
|
|
@@ -169,7 +179,7 @@ Environment=FZ_SEED_CREDENTIAL=agent-seed
|
|
|
169
179
|
Environment=FZ_ENROL_TOKEN_CREDENTIAL=enrol-token
|
|
170
180
|
Environment=FZ_ENROL_STATE_FILE=${options.enrolStatePath}
|
|
171
181
|
Environment=FZ_API=${options.apiUrl}
|
|
172
|
-
${label}${gitPublicKey}ExecStart=${bin} enrol
|
|
182
|
+
${label}${gitPublicKey}${networkAttachment}ExecStart=${bin} enrol
|
|
173
183
|
# A '+' fixed command runs as root solely to remove the host-bound one-time
|
|
174
184
|
# ciphertext. Tenant code and the agent never receive a privilege boundary.
|
|
175
185
|
ExecStartPost=+/usr/bin/rm -f ${options.enrolTokenCredentialPath}
|
|
@@ -247,6 +257,21 @@ function agentUnit(options) {
|
|
|
247
257
|
const warpEnabled = warpValues.every(Boolean);
|
|
248
258
|
if (warpValues.some(Boolean) && !warpEnabled)
|
|
249
259
|
throw new Error("WARP configuration must be supplied together");
|
|
260
|
+
const networkAttachmentValues = [
|
|
261
|
+
options.cloudflareAccountId,
|
|
262
|
+
options.cloudflareTunnelId,
|
|
263
|
+
options.cloudflareVirtualNetworkId,
|
|
264
|
+
options.cloudflareWarpPolicyId
|
|
265
|
+
];
|
|
266
|
+
if (networkAttachmentValues.some(Boolean)) {
|
|
267
|
+
if (!options.cloudflareAccountId || !options.cloudflareTunnelId || !options.cloudflareWarpPolicyId) {
|
|
268
|
+
throw new Error("private-network attachment requires account, Tunnel and WARP policy ids");
|
|
269
|
+
}
|
|
270
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
271
|
+
if (!/^[a-f0-9]{32}$/i.test(options.cloudflareAccountId) || !uuid.test(options.cloudflareTunnelId) || options.cloudflareVirtualNetworkId && !uuid.test(options.cloudflareVirtualNetworkId) || !/^[A-Za-z0-9-]{1,64}$/.test(options.cloudflareWarpPolicyId)) {
|
|
272
|
+
throw new Error("private-network attachment coordinates are invalid");
|
|
273
|
+
}
|
|
274
|
+
}
|
|
250
275
|
const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath && options.enrolStatePath);
|
|
251
276
|
const deploymentEnvironment = options.deploymentEnvironment ?? {};
|
|
252
277
|
const deploymentCredentials = options.deploymentCredentials ?? {};
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** One package version shared by both public binaries. Pinned to package.json by tests. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.24";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "Publishing happens from an operator's machine, not CI \u2014 CLAUDE.md records that the absence of CI is deliberate. npm's `provenance` attests a tarball was built by a recognised CI provider from a named commit, so it cannot be produced here: it was set, and the first publish failed with `Automatic provenance generation not supported for provider: null`. A setting that can never be satisfied is worse than none, because it reads as a guarantee nobody is getting. Restore it the day this publishes from CI, and not before.",
|
|
3
3
|
"name": "@forgezero/agent",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.24",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"check": "tsc --noEmit",
|