@forgezero/agent 0.1.16 → 0.1.17

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.
@@ -13,11 +13,12 @@ export declare function startDeploymentRunner(options: {
13
13
  root: string;
14
14
  home: string;
15
15
  socketPath?: string;
16
- /** systemd socket activation passes the agent-owned listener as fd 3. */
17
- listenFd?: number;
16
+ /** Bind closed, then let the service manager transfer access to the Agent. */
17
+ socketMode?: number;
18
18
  exec?: (input: CommandInput, home: string) => Promise<CommandResult>;
19
19
  }): {
20
20
  server: Server;
21
+ ready: Promise<void>;
21
22
  stop(): Promise<void>;
22
23
  };
23
24
  export declare function requestDeploymentCommand(input: CommandInput, socketPath?: string): Promise<CommandResult>;
@@ -103,7 +103,7 @@ function startDeploymentRunner(options) {
103
103
  const root = resolve(options.root);
104
104
  const home = resolve(options.home);
105
105
  const socketPath = options.socketPath ?? DEFAULT_DEPLOYMENT_RUNNER_SOCKET;
106
- if (options.listenFd === undefined && existsSync(socketPath))
106
+ if (existsSync(socketPath))
107
107
  unlinkSync(socketPath);
108
108
  const active = new Set;
109
109
  const server = createServer((socket) => {
@@ -145,12 +145,17 @@ function startDeploymentRunner(options) {
145
145
  });
146
146
  socket.on("error", () => socket.destroy());
147
147
  });
148
- if (options.listenFd !== undefined)
149
- server.listen({ fd: options.listenFd });
150
- else
151
- server.listen(socketPath, () => chmodSync(socketPath, 432));
148
+ const ready = new Promise((resolveReady, rejectReady) => {
149
+ server.once("error", rejectReady);
150
+ server.listen(socketPath, () => {
151
+ chmodSync(socketPath, options.socketMode ?? 432);
152
+ server.off("error", rejectReady);
153
+ resolveReady();
154
+ });
155
+ });
152
156
  return {
153
157
  server,
158
+ ready,
154
159
  async stop() {
155
160
  await new Promise((resolveStop) => server.close(() => resolveStop()));
156
161
  await Promise.all(active);
package/dist/fz-agent.js CHANGED
@@ -1495,38 +1495,29 @@ var DEPLOYMENT_RUNNER_USER = "forgezero-runner";
1495
1495
  var DEPLOYMENT_GROUP = "forgezero-deploy";
1496
1496
  var VAULT_GROUP = "forgezero-vault";
1497
1497
  var DEPLOYMENT_RUNNER_SOCKET = "/run/forgezero-deploy/runner.sock";
1498
- function deploymentRunnerSocketUnit(agentUser) {
1499
- return `[Unit]
1500
- Description=ForgeZero private project-command socket
1501
-
1502
- [Socket]
1503
- ListenStream=${DEPLOYMENT_RUNNER_SOCKET}
1504
- SocketUser=${agentUser}
1505
- SocketGroup=${agentUser}
1506
- SocketMode=0600
1507
- DirectoryMode=0710
1508
- RemoveOnStop=true
1509
-
1510
- [Install]
1511
- WantedBy=sockets.target
1512
- `;
1513
- }
1514
1498
  function deploymentRunnerUnit(options) {
1515
1499
  const bin = options.binPath ?? "fz-agent";
1516
1500
  const root = options.deployRoot ?? "/opt/forgezero";
1501
+ const agentUser = options.user ?? "forgezero-agent";
1517
1502
  return `[Unit]
1518
1503
  Description=ForgeZero credential-free project command runner
1519
1504
  Documentation=https://www.forgezero.net/docs/agent
1520
- After=forgezero-deploy-runner.socket
1521
- Requires=forgezero-deploy-runner.socket
1522
1505
 
1523
1506
  [Service]
1524
- Type=simple
1507
+ Type=notify
1508
+ NotifyAccess=all
1525
1509
  User=${DEPLOYMENT_RUNNER_USER}
1526
1510
  Group=${DEPLOYMENT_GROUP}
1527
1511
  Environment=FZ_DEPLOY_RUNNER_SOCKET=${DEPLOYMENT_RUNNER_SOCKET}
1528
- Sockets=forgezero-deploy-runner.socket
1512
+ RuntimeDirectory=forgezero-deploy
1513
+ RuntimeDirectoryMode=0710
1514
+ ExecStartPre=+/usr/bin/install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0710 /run/forgezero-deploy
1515
+ ExecStartPre=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
1529
1516
  ExecStart=${bin} deploy-runner --root=${root} --home=${root}/runner-home
1517
+ ExecStartPost=+/usr/bin/chown ${agentUser}:${agentUser} ${DEPLOYMENT_RUNNER_SOCKET}
1518
+ ExecStartPost=+/usr/bin/chmod 0600 ${DEPLOYMENT_RUNNER_SOCKET}
1519
+ ExecStartPost=+/usr/bin/chown root:root /run/forgezero-deploy
1520
+ ExecStopPost=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
1530
1521
  Restart=always
1531
1522
  RestartSec=2
1532
1523
  UMask=0007
@@ -1797,7 +1788,10 @@ if [[ ! -s /etc/forgezero/git/deploy.pub ]]; then
1797
1788
  fi
1798
1789
  chmod 0400 /etc/forgezero/creds/git-deploy-key.cred
1799
1790
  systemctl daemon-reload
1800
- systemctl enable --now forgezero-deploy-runner.socket forgezero-deploy-runner.service forgezero-agent.service
1791
+ systemctl disable --now forgezero-deploy-runner.socket 2>/dev/null || true
1792
+ rm -f /etc/systemd/system/forgezero-deploy-runner.socket
1793
+ systemctl daemon-reload
1794
+ systemctl enable --now forgezero-deploy-runner.service forgezero-agent.service
1801
1795
  `;
1802
1796
  }
1803
1797
  function guestAgentUnit(profile, name, attested = Boolean(profile.confidential), pull = true) {
@@ -1891,8 +1885,7 @@ function cloudInit(profile, claim, manifest) {
1891
1885
  const enrolmentDropIn = guestEnrolmentDropIn();
1892
1886
  const cleanupScript = guestEnrolmentCleanupScript();
1893
1887
  const cleanupUnit = guestEnrolmentCleanupUnit();
1894
- const runnerSocketUnit = deploymentRunnerSocketUnit("forgezero-agent");
1895
- const runnerUnit = deploymentRunnerUnit({ binPath: "/usr/local/bin/fz-agent", deployRoot: "/opt/forgezero" });
1888
+ const runnerUnit = deploymentRunnerUnit({ binPath: "/usr/local/bin/fz-agent", deployRoot: "/opt/forgezero", user: "forgezero-agent" });
1896
1889
  const access = claim.access;
1897
1890
  const users = access ? `users:
1898
1891
  - default
@@ -1914,7 +1907,7 @@ ${users}disable_root: true
1914
1907
  # every dynamically claimed repository must be cloneable on a clean image.
1915
1908
  packages: [curl, ca-certificates, openssl, openssh-client, git, unzip${claim.spec.confidential ? ", python3" : ""}]
1916
1909
  write_files:
1917
- ${enrolmentFiles}${yamlFile("/usr/local/sbin/forgezero-guest-bootstrap", bootstrap, "0700")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.socket", runnerSocketUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.service", runnerUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-agent.service", agentUnit, "0644")}runcmd:
1910
+ ${enrolmentFiles}${yamlFile("/usr/local/sbin/forgezero-guest-bootstrap", bootstrap, "0700")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.service", runnerUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-agent.service", agentUnit, "0644")}runcmd:
1918
1911
  - [ bash, /usr/local/sbin/forgezero-guest-bootstrap ]
1919
1912
  ${enrolled ? ` - [ systemctl, enable, --now, forgezero-enrolment-cleanup.service ]
1920
1913
  ` : ""}
@@ -2350,7 +2343,7 @@ function startDeploymentRunner(options) {
2350
2343
  const root = resolve(options.root);
2351
2344
  const home = resolve(options.home);
2352
2345
  const socketPath = options.socketPath ?? DEFAULT_DEPLOYMENT_RUNNER_SOCKET;
2353
- if (options.listenFd === undefined && existsSync7(socketPath))
2346
+ if (existsSync7(socketPath))
2354
2347
  unlinkSync6(socketPath);
2355
2348
  const active = new Set;
2356
2349
  const server = createServer4((socket) => {
@@ -2392,12 +2385,17 @@ function startDeploymentRunner(options) {
2392
2385
  });
2393
2386
  socket.on("error", () => socket.destroy());
2394
2387
  });
2395
- if (options.listenFd !== undefined)
2396
- server.listen({ fd: options.listenFd });
2397
- else
2398
- server.listen(socketPath, () => chmodSync6(socketPath, 432));
2388
+ const ready = new Promise((resolveReady, rejectReady) => {
2389
+ server.once("error", rejectReady);
2390
+ server.listen(socketPath, () => {
2391
+ chmodSync6(socketPath, options.socketMode ?? 432);
2392
+ server.off("error", rejectReady);
2393
+ resolveReady();
2394
+ });
2395
+ });
2399
2396
  return {
2400
2397
  server,
2398
+ ready,
2401
2399
  async stop() {
2402
2400
  await new Promise((resolveStop) => server.close(() => resolveStop()));
2403
2401
  await Promise.all(active);
@@ -2713,7 +2711,7 @@ async function applyMetalIsolation(profile, exec = defaultExec) {
2713
2711
  }
2714
2712
 
2715
2713
  // src/version.ts
2716
- var VERSION = "0.1.16";
2714
+ var VERSION = "0.1.17";
2717
2715
 
2718
2716
  // src/index.ts
2719
2717
  function loadOrCreateSeed(path) {
@@ -2901,8 +2899,14 @@ if (import.meta.main) {
2901
2899
  root: `${root2.replace(/\/$/, "")}/releases`,
2902
2900
  home,
2903
2901
  socketPath: process.env.FZ_DEPLOY_RUNNER_SOCKET ?? DEFAULT_DEPLOYMENT_RUNNER_SOCKET,
2904
- listenFd: Number(process.env.LISTEN_PID) === process.pid && Number(process.env.LISTEN_FDS) > 0 ? 3 : undefined
2902
+ socketMode: process.env.NOTIFY_SOCKET ? 0 : 432
2905
2903
  });
2904
+ await runner.ready;
2905
+ if (process.env.NOTIFY_SOCKET) {
2906
+ const notification = Bun.spawnSync(["/usr/bin/systemd-notify", "--ready"]);
2907
+ if (notification.exitCode !== 0)
2908
+ throw new Error("could not notify systemd that the deployment runner is ready");
2909
+ }
2906
2910
  console.log(`[deploy-runner] listening on ${process.env.FZ_DEPLOY_RUNNER_SOCKET ?? DEFAULT_DEPLOYMENT_RUNNER_SOCKET}`);
2907
2911
  let stopping = false;
2908
2912
  const stop = async () => {
package/dist/fz.js CHANGED
@@ -150,7 +150,6 @@ var DEPLOYMENT_RUNNER_USER = "forgezero-runner";
150
150
  var DEPLOYMENT_GROUP = "forgezero-deploy";
151
151
  var VAULT_GROUP = "forgezero-vault";
152
152
  var DEPLOYMENT_RUNNER_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.service";
153
- var DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.socket";
154
153
  var DEPLOYMENT_RUNNER_SOCKET = "/run/forgezero-deploy/runner.sock";
155
154
  var ENROLMENT_UNIT_PATH = "/etc/systemd/system/forgezero-agent-enrol.service";
156
155
  function agentEnrolmentUnit(options) {
@@ -197,38 +196,29 @@ LimitCORE=0
197
196
  WantedBy=multi-user.target
198
197
  `;
199
198
  }
200
- function deploymentRunnerSocketUnit(agentUser) {
201
- return `[Unit]
202
- Description=ForgeZero private project-command socket
203
-
204
- [Socket]
205
- ListenStream=${DEPLOYMENT_RUNNER_SOCKET}
206
- SocketUser=${agentUser}
207
- SocketGroup=${agentUser}
208
- SocketMode=0600
209
- DirectoryMode=0710
210
- RemoveOnStop=true
211
-
212
- [Install]
213
- WantedBy=sockets.target
214
- `;
215
- }
216
199
  function deploymentRunnerUnit(options) {
217
200
  const bin = options.binPath ?? "fz-agent";
218
201
  const root = options.deployRoot ?? "/opt/forgezero";
202
+ const agentUser = options.user ?? "forgezero-agent";
219
203
  return `[Unit]
220
204
  Description=ForgeZero credential-free project command runner
221
205
  Documentation=https://www.forgezero.net/docs/agent
222
- After=forgezero-deploy-runner.socket
223
- Requires=forgezero-deploy-runner.socket
224
206
 
225
207
  [Service]
226
- Type=simple
208
+ Type=notify
209
+ NotifyAccess=all
227
210
  User=${DEPLOYMENT_RUNNER_USER}
228
211
  Group=${DEPLOYMENT_GROUP}
229
212
  Environment=FZ_DEPLOY_RUNNER_SOCKET=${DEPLOYMENT_RUNNER_SOCKET}
230
- Sockets=forgezero-deploy-runner.socket
213
+ RuntimeDirectory=forgezero-deploy
214
+ RuntimeDirectoryMode=0710
215
+ ExecStartPre=+/usr/bin/install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0710 /run/forgezero-deploy
216
+ ExecStartPre=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
231
217
  ExecStart=${bin} deploy-runner --root=${root} --home=${root}/runner-home
218
+ ExecStartPost=+/usr/bin/chown ${agentUser}:${agentUser} ${DEPLOYMENT_RUNNER_SOCKET}
219
+ ExecStartPost=+/usr/bin/chmod 0600 ${DEPLOYMENT_RUNNER_SOCKET}
220
+ ExecStartPost=+/usr/bin/chown root:root /run/forgezero-deploy
221
+ ExecStopPost=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
232
222
  Restart=always
233
223
  RestartSec=2
234
224
  UMask=0007
@@ -406,7 +396,6 @@ function planProvision(options) {
406
396
  unit: agentUnit({ ...options, mode }),
407
397
  auxiliaryUnits: [
408
398
  ...deploymentEnabled ? [
409
- { path: DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH, unit: deploymentRunnerSocketUnit(user) },
410
399
  { path: DEPLOYMENT_RUNNER_UNIT_PATH, unit: deploymentRunnerUnit(options) }
411
400
  ] : [],
412
401
  ...enrolmentEnabled ? [
@@ -473,10 +462,14 @@ function planProvision(options) {
473
462
  command: `install -d -o root -g root -m 0755 ${deployRoot} && ` + `install -d -o root -g ${DEPLOYMENT_GROUP} -m 3770 ${deployRoot}/releases && ` + `install -d -o ${user} -g ${user} -m 0750 ${deployRoot}/cache && ` + `install -d -o ${user} -g ${user} -m 0700 ${deployRoot}/agent-home && ` + `install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0700 ${deployRoot}/runner-home ${deployRoot}/runner-home/cache`
474
463
  }] : [],
475
464
  { label: "reload units", command: "systemctl daemon-reload" },
465
+ ...deploymentEnabled ? [{
466
+ label: "remove unsupported deployment socket activation",
467
+ command: "systemctl disable --now forgezero-deploy-runner.socket 2>/dev/null || true; rm -f /etc/systemd/system/forgezero-deploy-runner.socket; systemctl daemon-reload"
468
+ }] : [],
476
469
  {
477
470
  label: "enable and start",
478
471
  command: `systemctl enable --now ${[
479
- ...deploymentEnabled ? ["forgezero-deploy-runner.socket", "forgezero-deploy-runner.service"] : [],
472
+ ...deploymentEnabled ? ["forgezero-deploy-runner.service"] : [],
480
473
  ...enrolmentEnabled ? ["forgezero-agent-enrol.service"] : [],
481
474
  "forgezero-agent.service"
482
475
  ].join(" ")}`
@@ -781,7 +774,7 @@ async function resolveIdentity(selector, socketPath) {
781
774
  }
782
775
 
783
776
  // src/version.ts
784
- var VERSION = "0.1.16";
777
+ var VERSION = "0.1.17";
785
778
 
786
779
  // src/cli/index.ts
787
780
  var DEFAULT_MODE = THRESHOLD_MODES[0].id;
@@ -183,7 +183,6 @@ var DEPLOYMENT_RUNNER_USER = "forgezero-runner";
183
183
  var DEPLOYMENT_GROUP = "forgezero-deploy";
184
184
  var VAULT_GROUP = "forgezero-vault";
185
185
  var DEPLOYMENT_RUNNER_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.service";
186
- var DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.socket";
187
186
  var DEPLOYMENT_RUNNER_SOCKET = "/run/forgezero-deploy/runner.sock";
188
187
  var ENROLMENT_UNIT_PATH = "/etc/systemd/system/forgezero-agent-enrol.service";
189
188
  function agentEnrolmentUnit(options) {
@@ -230,38 +229,29 @@ LimitCORE=0
230
229
  WantedBy=multi-user.target
231
230
  `;
232
231
  }
233
- function deploymentRunnerSocketUnit(agentUser) {
234
- return `[Unit]
235
- Description=ForgeZero private project-command socket
236
-
237
- [Socket]
238
- ListenStream=${DEPLOYMENT_RUNNER_SOCKET}
239
- SocketUser=${agentUser}
240
- SocketGroup=${agentUser}
241
- SocketMode=0600
242
- DirectoryMode=0710
243
- RemoveOnStop=true
244
-
245
- [Install]
246
- WantedBy=sockets.target
247
- `;
248
- }
249
232
  function deploymentRunnerUnit(options) {
250
233
  const bin = options.binPath ?? "fz-agent";
251
234
  const root = options.deployRoot ?? "/opt/forgezero";
235
+ const agentUser = options.user ?? "forgezero-agent";
252
236
  return `[Unit]
253
237
  Description=ForgeZero credential-free project command runner
254
238
  Documentation=https://www.forgezero.net/docs/agent
255
- After=forgezero-deploy-runner.socket
256
- Requires=forgezero-deploy-runner.socket
257
239
 
258
240
  [Service]
259
- Type=simple
241
+ Type=notify
242
+ NotifyAccess=all
260
243
  User=${DEPLOYMENT_RUNNER_USER}
261
244
  Group=${DEPLOYMENT_GROUP}
262
245
  Environment=FZ_DEPLOY_RUNNER_SOCKET=${DEPLOYMENT_RUNNER_SOCKET}
263
- Sockets=forgezero-deploy-runner.socket
246
+ RuntimeDirectory=forgezero-deploy
247
+ RuntimeDirectoryMode=0710
248
+ ExecStartPre=+/usr/bin/install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0710 /run/forgezero-deploy
249
+ ExecStartPre=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
264
250
  ExecStart=${bin} deploy-runner --root=${root} --home=${root}/runner-home
251
+ ExecStartPost=+/usr/bin/chown ${agentUser}:${agentUser} ${DEPLOYMENT_RUNNER_SOCKET}
252
+ ExecStartPost=+/usr/bin/chmod 0600 ${DEPLOYMENT_RUNNER_SOCKET}
253
+ ExecStartPost=+/usr/bin/chown root:root /run/forgezero-deploy
254
+ ExecStopPost=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
265
255
  Restart=always
266
256
  RestartSec=2
267
257
  UMask=0007
@@ -439,7 +429,6 @@ function planProvision(options) {
439
429
  unit: agentUnit({ ...options, mode }),
440
430
  auxiliaryUnits: [
441
431
  ...deploymentEnabled ? [
442
- { path: DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH, unit: deploymentRunnerSocketUnit(user) },
443
432
  { path: DEPLOYMENT_RUNNER_UNIT_PATH, unit: deploymentRunnerUnit(options) }
444
433
  ] : [],
445
434
  ...enrolmentEnabled ? [
@@ -506,10 +495,14 @@ function planProvision(options) {
506
495
  command: `install -d -o root -g root -m 0755 ${deployRoot} && ` + `install -d -o root -g ${DEPLOYMENT_GROUP} -m 3770 ${deployRoot}/releases && ` + `install -d -o ${user} -g ${user} -m 0750 ${deployRoot}/cache && ` + `install -d -o ${user} -g ${user} -m 0700 ${deployRoot}/agent-home && ` + `install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0700 ${deployRoot}/runner-home ${deployRoot}/runner-home/cache`
507
496
  }] : [],
508
497
  { label: "reload units", command: "systemctl daemon-reload" },
498
+ ...deploymentEnabled ? [{
499
+ label: "remove unsupported deployment socket activation",
500
+ command: "systemctl disable --now forgezero-deploy-runner.socket 2>/dev/null || true; rm -f /etc/systemd/system/forgezero-deploy-runner.socket; systemctl daemon-reload"
501
+ }] : [],
509
502
  {
510
503
  label: "enable and start",
511
504
  command: `systemctl enable --now ${[
512
- ...deploymentEnabled ? ["forgezero-deploy-runner.socket", "forgezero-deploy-runner.service"] : [],
505
+ ...deploymentEnabled ? ["forgezero-deploy-runner.service"] : [],
513
506
  ...enrolmentEnabled ? ["forgezero-agent-enrol.service"] : [],
514
507
  "forgezero-agent.service"
515
508
  ].join(" ")}`
@@ -793,7 +786,10 @@ if [[ ! -s /etc/forgezero/git/deploy.pub ]]; then
793
786
  fi
794
787
  chmod 0400 /etc/forgezero/creds/git-deploy-key.cred
795
788
  systemctl daemon-reload
796
- systemctl enable --now forgezero-deploy-runner.socket forgezero-deploy-runner.service forgezero-agent.service
789
+ systemctl disable --now forgezero-deploy-runner.socket 2>/dev/null || true
790
+ rm -f /etc/systemd/system/forgezero-deploy-runner.socket
791
+ systemctl daemon-reload
792
+ systemctl enable --now forgezero-deploy-runner.service forgezero-agent.service
797
793
  `;
798
794
  }
799
795
  function guestAgentUnit(profile, name, attested = Boolean(profile.confidential), pull = true) {
@@ -887,8 +883,7 @@ function cloudInit(profile, claim, manifest) {
887
883
  const enrolmentDropIn = guestEnrolmentDropIn();
888
884
  const cleanupScript = guestEnrolmentCleanupScript();
889
885
  const cleanupUnit = guestEnrolmentCleanupUnit();
890
- const runnerSocketUnit = deploymentRunnerSocketUnit("forgezero-agent");
891
- const runnerUnit = deploymentRunnerUnit({ binPath: "/usr/local/bin/fz-agent", deployRoot: "/opt/forgezero" });
886
+ const runnerUnit = deploymentRunnerUnit({ binPath: "/usr/local/bin/fz-agent", deployRoot: "/opt/forgezero", user: "forgezero-agent" });
892
887
  const access = claim.access;
893
888
  const users = access ? `users:
894
889
  - default
@@ -910,7 +905,7 @@ ${users}disable_root: true
910
905
  # every dynamically claimed repository must be cloneable on a clean image.
911
906
  packages: [curl, ca-certificates, openssl, openssh-client, git, unzip${claim.spec.confidential ? ", python3" : ""}]
912
907
  write_files:
913
- ${enrolmentFiles}${yamlFile("/usr/local/sbin/forgezero-guest-bootstrap", bootstrap, "0700")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.socket", runnerSocketUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.service", runnerUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-agent.service", agentUnit2, "0644")}runcmd:
908
+ ${enrolmentFiles}${yamlFile("/usr/local/sbin/forgezero-guest-bootstrap", bootstrap, "0700")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.service", runnerUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-agent.service", agentUnit2, "0644")}runcmd:
914
909
  - [ bash, /usr/local/sbin/forgezero-guest-bootstrap ]
915
910
  ${enrolled ? ` - [ systemctl, enable, --now, forgezero-enrolment-cleanup.service ]
916
911
  ` : ""}
@@ -183,7 +183,6 @@ var DEPLOYMENT_RUNNER_USER = "forgezero-runner";
183
183
  var DEPLOYMENT_GROUP = "forgezero-deploy";
184
184
  var VAULT_GROUP = "forgezero-vault";
185
185
  var DEPLOYMENT_RUNNER_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.service";
186
- var DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.socket";
187
186
  var DEPLOYMENT_RUNNER_SOCKET = "/run/forgezero-deploy/runner.sock";
188
187
  var ENROLMENT_UNIT_PATH = "/etc/systemd/system/forgezero-agent-enrol.service";
189
188
  function agentEnrolmentUnit(options) {
@@ -230,38 +229,29 @@ LimitCORE=0
230
229
  WantedBy=multi-user.target
231
230
  `;
232
231
  }
233
- function deploymentRunnerSocketUnit(agentUser) {
234
- return `[Unit]
235
- Description=ForgeZero private project-command socket
236
-
237
- [Socket]
238
- ListenStream=${DEPLOYMENT_RUNNER_SOCKET}
239
- SocketUser=${agentUser}
240
- SocketGroup=${agentUser}
241
- SocketMode=0600
242
- DirectoryMode=0710
243
- RemoveOnStop=true
244
-
245
- [Install]
246
- WantedBy=sockets.target
247
- `;
248
- }
249
232
  function deploymentRunnerUnit(options) {
250
233
  const bin = options.binPath ?? "fz-agent";
251
234
  const root = options.deployRoot ?? "/opt/forgezero";
235
+ const agentUser = options.user ?? "forgezero-agent";
252
236
  return `[Unit]
253
237
  Description=ForgeZero credential-free project command runner
254
238
  Documentation=https://www.forgezero.net/docs/agent
255
- After=forgezero-deploy-runner.socket
256
- Requires=forgezero-deploy-runner.socket
257
239
 
258
240
  [Service]
259
- Type=simple
241
+ Type=notify
242
+ NotifyAccess=all
260
243
  User=${DEPLOYMENT_RUNNER_USER}
261
244
  Group=${DEPLOYMENT_GROUP}
262
245
  Environment=FZ_DEPLOY_RUNNER_SOCKET=${DEPLOYMENT_RUNNER_SOCKET}
263
- Sockets=forgezero-deploy-runner.socket
246
+ RuntimeDirectory=forgezero-deploy
247
+ RuntimeDirectoryMode=0710
248
+ ExecStartPre=+/usr/bin/install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0710 /run/forgezero-deploy
249
+ ExecStartPre=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
264
250
  ExecStart=${bin} deploy-runner --root=${root} --home=${root}/runner-home
251
+ ExecStartPost=+/usr/bin/chown ${agentUser}:${agentUser} ${DEPLOYMENT_RUNNER_SOCKET}
252
+ ExecStartPost=+/usr/bin/chmod 0600 ${DEPLOYMENT_RUNNER_SOCKET}
253
+ ExecStartPost=+/usr/bin/chown root:root /run/forgezero-deploy
254
+ ExecStopPost=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
265
255
  Restart=always
266
256
  RestartSec=2
267
257
  UMask=0007
@@ -439,7 +429,6 @@ function planProvision(options) {
439
429
  unit: agentUnit({ ...options, mode }),
440
430
  auxiliaryUnits: [
441
431
  ...deploymentEnabled ? [
442
- { path: DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH, unit: deploymentRunnerSocketUnit(user) },
443
432
  { path: DEPLOYMENT_RUNNER_UNIT_PATH, unit: deploymentRunnerUnit(options) }
444
433
  ] : [],
445
434
  ...enrolmentEnabled ? [
@@ -506,10 +495,14 @@ function planProvision(options) {
506
495
  command: `install -d -o root -g root -m 0755 ${deployRoot} && ` + `install -d -o root -g ${DEPLOYMENT_GROUP} -m 3770 ${deployRoot}/releases && ` + `install -d -o ${user} -g ${user} -m 0750 ${deployRoot}/cache && ` + `install -d -o ${user} -g ${user} -m 0700 ${deployRoot}/agent-home && ` + `install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0700 ${deployRoot}/runner-home ${deployRoot}/runner-home/cache`
507
496
  }] : [],
508
497
  { label: "reload units", command: "systemctl daemon-reload" },
498
+ ...deploymentEnabled ? [{
499
+ label: "remove unsupported deployment socket activation",
500
+ command: "systemctl disable --now forgezero-deploy-runner.socket 2>/dev/null || true; rm -f /etc/systemd/system/forgezero-deploy-runner.socket; systemctl daemon-reload"
501
+ }] : [],
509
502
  {
510
503
  label: "enable and start",
511
504
  command: `systemctl enable --now ${[
512
- ...deploymentEnabled ? ["forgezero-deploy-runner.socket", "forgezero-deploy-runner.service"] : [],
505
+ ...deploymentEnabled ? ["forgezero-deploy-runner.service"] : [],
513
506
  ...enrolmentEnabled ? ["forgezero-agent-enrol.service"] : [],
514
507
  "forgezero-agent.service"
515
508
  ].join(" ")}`
@@ -793,7 +786,10 @@ if [[ ! -s /etc/forgezero/git/deploy.pub ]]; then
793
786
  fi
794
787
  chmod 0400 /etc/forgezero/creds/git-deploy-key.cred
795
788
  systemctl daemon-reload
796
- systemctl enable --now forgezero-deploy-runner.socket forgezero-deploy-runner.service forgezero-agent.service
789
+ systemctl disable --now forgezero-deploy-runner.socket 2>/dev/null || true
790
+ rm -f /etc/systemd/system/forgezero-deploy-runner.socket
791
+ systemctl daemon-reload
792
+ systemctl enable --now forgezero-deploy-runner.service forgezero-agent.service
797
793
  `;
798
794
  }
799
795
  function guestAgentUnit(profile, name, attested = Boolean(profile.confidential), pull = true) {
@@ -887,8 +883,7 @@ function cloudInit(profile, claim, manifest) {
887
883
  const enrolmentDropIn = guestEnrolmentDropIn();
888
884
  const cleanupScript = guestEnrolmentCleanupScript();
889
885
  const cleanupUnit = guestEnrolmentCleanupUnit();
890
- const runnerSocketUnit = deploymentRunnerSocketUnit("forgezero-agent");
891
- const runnerUnit = deploymentRunnerUnit({ binPath: "/usr/local/bin/fz-agent", deployRoot: "/opt/forgezero" });
886
+ const runnerUnit = deploymentRunnerUnit({ binPath: "/usr/local/bin/fz-agent", deployRoot: "/opt/forgezero", user: "forgezero-agent" });
892
887
  const access = claim.access;
893
888
  const users = access ? `users:
894
889
  - default
@@ -910,7 +905,7 @@ ${users}disable_root: true
910
905
  # every dynamically claimed repository must be cloneable on a clean image.
911
906
  packages: [curl, ca-certificates, openssl, openssh-client, git, unzip${claim.spec.confidential ? ", python3" : ""}]
912
907
  write_files:
913
- ${enrolmentFiles}${yamlFile("/usr/local/sbin/forgezero-guest-bootstrap", bootstrap, "0700")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.socket", runnerSocketUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.service", runnerUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-agent.service", agentUnit2, "0644")}runcmd:
908
+ ${enrolmentFiles}${yamlFile("/usr/local/sbin/forgezero-guest-bootstrap", bootstrap, "0700")}${yamlFile("/etc/systemd/system/forgezero-deploy-runner.service", runnerUnit, "0644")}${yamlFile("/etc/systemd/system/forgezero-agent.service", agentUnit2, "0644")}runcmd:
914
909
  - [ bash, /usr/local/sbin/forgezero-guest-bootstrap ]
915
910
  ${enrolled ? ` - [ systemctl, enable, --now, forgezero-enrolment-cleanup.service ]
916
911
  ` : ""}
@@ -110,7 +110,6 @@ export declare const DEPLOYMENT_RUNNER_USER = "forgezero-runner";
110
110
  export declare const DEPLOYMENT_GROUP = "forgezero-deploy";
111
111
  export declare const VAULT_GROUP = "forgezero-vault";
112
112
  export declare const DEPLOYMENT_RUNNER_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.service";
113
- export declare const DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.socket";
114
113
  export declare const DEPLOYMENT_RUNNER_SOCKET = "/run/forgezero-deploy/runner.sock";
115
114
  export declare const ENROLMENT_UNIT_PATH = "/etc/systemd/system/forgezero-agent-enrol.service";
116
115
  /**
@@ -121,8 +120,7 @@ export declare const ENROLMENT_UNIT_PATH = "/etc/systemd/system/forgezero-agent-
121
120
  * the oneshot skip; a failed exchange keeps the encrypted token for a retry.
122
121
  */
123
122
  export declare function agentEnrolmentUnit(options: UnitOptions): string;
124
- export declare function deploymentRunnerSocketUnit(agentUser: string): string;
125
- export declare function deploymentRunnerUnit(options: Pick<UnitOptions, 'binPath' | 'deployRoot'>): string;
123
+ export declare function deploymentRunnerUnit(options: Pick<UnitOptions, 'binPath' | 'deployRoot' | 'user'>): string;
126
124
  /**
127
125
  * A systemd unit for the agent.
128
126
  *
package/dist/provision.js CHANGED
@@ -43,7 +43,6 @@ var DEPLOYMENT_RUNNER_USER = "forgezero-runner";
43
43
  var DEPLOYMENT_GROUP = "forgezero-deploy";
44
44
  var VAULT_GROUP = "forgezero-vault";
45
45
  var DEPLOYMENT_RUNNER_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.service";
46
- var DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH = "/etc/systemd/system/forgezero-deploy-runner.socket";
47
46
  var DEPLOYMENT_RUNNER_SOCKET = "/run/forgezero-deploy/runner.sock";
48
47
  var ENROLMENT_UNIT_PATH = "/etc/systemd/system/forgezero-agent-enrol.service";
49
48
  function agentEnrolmentUnit(options) {
@@ -90,38 +89,29 @@ LimitCORE=0
90
89
  WantedBy=multi-user.target
91
90
  `;
92
91
  }
93
- function deploymentRunnerSocketUnit(agentUser) {
94
- return `[Unit]
95
- Description=ForgeZero private project-command socket
96
-
97
- [Socket]
98
- ListenStream=${DEPLOYMENT_RUNNER_SOCKET}
99
- SocketUser=${agentUser}
100
- SocketGroup=${agentUser}
101
- SocketMode=0600
102
- DirectoryMode=0710
103
- RemoveOnStop=true
104
-
105
- [Install]
106
- WantedBy=sockets.target
107
- `;
108
- }
109
92
  function deploymentRunnerUnit(options) {
110
93
  const bin = options.binPath ?? "fz-agent";
111
94
  const root = options.deployRoot ?? "/opt/forgezero";
95
+ const agentUser = options.user ?? "forgezero-agent";
112
96
  return `[Unit]
113
97
  Description=ForgeZero credential-free project command runner
114
98
  Documentation=https://www.forgezero.net/docs/agent
115
- After=forgezero-deploy-runner.socket
116
- Requires=forgezero-deploy-runner.socket
117
99
 
118
100
  [Service]
119
- Type=simple
101
+ Type=notify
102
+ NotifyAccess=all
120
103
  User=${DEPLOYMENT_RUNNER_USER}
121
104
  Group=${DEPLOYMENT_GROUP}
122
105
  Environment=FZ_DEPLOY_RUNNER_SOCKET=${DEPLOYMENT_RUNNER_SOCKET}
123
- Sockets=forgezero-deploy-runner.socket
106
+ RuntimeDirectory=forgezero-deploy
107
+ RuntimeDirectoryMode=0710
108
+ ExecStartPre=+/usr/bin/install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0710 /run/forgezero-deploy
109
+ ExecStartPre=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
124
110
  ExecStart=${bin} deploy-runner --root=${root} --home=${root}/runner-home
111
+ ExecStartPost=+/usr/bin/chown ${agentUser}:${agentUser} ${DEPLOYMENT_RUNNER_SOCKET}
112
+ ExecStartPost=+/usr/bin/chmod 0600 ${DEPLOYMENT_RUNNER_SOCKET}
113
+ ExecStartPost=+/usr/bin/chown root:root /run/forgezero-deploy
114
+ ExecStopPost=+/usr/bin/rm -f ${DEPLOYMENT_RUNNER_SOCKET}
125
115
  Restart=always
126
116
  RestartSec=2
127
117
  UMask=0007
@@ -299,7 +289,6 @@ function planProvision(options) {
299
289
  unit: agentUnit({ ...options, mode }),
300
290
  auxiliaryUnits: [
301
291
  ...deploymentEnabled ? [
302
- { path: DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH, unit: deploymentRunnerSocketUnit(user) },
303
292
  { path: DEPLOYMENT_RUNNER_UNIT_PATH, unit: deploymentRunnerUnit(options) }
304
293
  ] : [],
305
294
  ...enrolmentEnabled ? [
@@ -366,10 +355,14 @@ function planProvision(options) {
366
355
  command: `install -d -o root -g root -m 0755 ${deployRoot} && ` + `install -d -o root -g ${DEPLOYMENT_GROUP} -m 3770 ${deployRoot}/releases && ` + `install -d -o ${user} -g ${user} -m 0750 ${deployRoot}/cache && ` + `install -d -o ${user} -g ${user} -m 0700 ${deployRoot}/agent-home && ` + `install -d -o ${DEPLOYMENT_RUNNER_USER} -g ${DEPLOYMENT_GROUP} -m 0700 ${deployRoot}/runner-home ${deployRoot}/runner-home/cache`
367
356
  }] : [],
368
357
  { label: "reload units", command: "systemctl daemon-reload" },
358
+ ...deploymentEnabled ? [{
359
+ label: "remove unsupported deployment socket activation",
360
+ command: "systemctl disable --now forgezero-deploy-runner.socket 2>/dev/null || true; rm -f /etc/systemd/system/forgezero-deploy-runner.socket; systemctl daemon-reload"
361
+ }] : [],
369
362
  {
370
363
  label: "enable and start",
371
364
  command: `systemctl enable --now ${[
372
- ...deploymentEnabled ? ["forgezero-deploy-runner.socket", "forgezero-deploy-runner.service"] : [],
365
+ ...deploymentEnabled ? ["forgezero-deploy-runner.service"] : [],
373
366
  ...enrolmentEnabled ? ["forgezero-agent-enrol.service"] : [],
374
367
  "forgezero-agent.service"
375
368
  ].join(" ")}`
@@ -396,7 +389,6 @@ export {
396
389
  planProvision,
397
390
  modeFor,
398
391
  deploymentRunnerUnit,
399
- deploymentRunnerSocketUnit,
400
392
  atLeast,
401
393
  agentUnit,
402
394
  agentEnrolmentUnit,
@@ -405,7 +397,6 @@ export {
405
397
  ENROLMENT_UNIT_PATH,
406
398
  DEPLOYMENT_RUNNER_USER,
407
399
  DEPLOYMENT_RUNNER_UNIT_PATH,
408
- DEPLOYMENT_RUNNER_SOCKET_UNIT_PATH,
409
400
  DEPLOYMENT_RUNNER_SOCKET,
410
401
  DEPLOYMENT_GROUP,
411
402
  CAPABILITY_CHECKS
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.16";
2
+ export declare const VERSION = "0.1.17";
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.16",
4
+ "version": "0.1.17",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "check": "tsc --noEmit",