You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Slow lane: simlock token create sometimes runs without the admin credential after a daemon restart #609
Seen twice on the slow lane while verifying PR #608, in two different tests of e2e/slow-agent-device.test.ts. Both times the failure came in setup, before any assertion:
Error: simlock token create failed: {"notice":"admin credential unavailable; connecting as agent (admin-only commands will fail with FORBIDDEN)"}
Run at 17:18 (log simlock-slow-e2e-20261015-171830.log): the test with agentDevice.command pointing at an agent-device that reports 0.9.2.
Run at 18:20 (log simlock-slow-e2e-20261015-182036.log): "with agentDevice.leaseTtlCapMs 20000, after a graceful simlock daemon restart an old simlock lease ... gets lease.expired".
Each test passes when run alone, and together with the test before it. It fails only in a full-file run, after many daemon restarts. token create ran as an agent because admin.token was missing at that moment. That points to a race in the e2e helpers (startDaemon / withConfig in e2e/helpers/env.ts) between a daemon restart and the admin credential being written. The code involved is the same on main.
Confirmed a deterministic credential-deletion race without hardware. The slow-lane failure itself was not rerun.
src/daemon/server.test.ts: “preserves the replacement daemon's admin credential when it starts as the old endpoint is released”. Two real endpoint hosts share credential storage. The replacement starts immediately after the old endpoint is released; its credential is verified before old shutdown resumes.
pnpm exec vitest run --project unit src/daemon/server.test.ts -t 'preserves the replacement daemon'
server.test.ts:982
expect(filesystem.readFile("/admin.token")).resolves.toBe(replacementToken)
AssertionError: promise rejected instead of resolving
Caused by: ENOENT: no such file or directory, open '/admin.token'
Defect.src/daemon/server.ts:656–659 releases the endpoint through host.stop() before removing admin.token. A replacement can claim the released endpoint and persist its own credential while old shutdown is suspended; the old daemon then deletes the replacement's file. The helper's process-exit wait cannot guarantee protection: e2e/helpers/env.ts:288–301 tolerates its timeout, and its PID lookup returns an empty list when unavailable. This confirms a production race consistent with the report, beyond the helper's existence check.
Simplest fix
Remove the old credential before releasing the endpoint, keeping credential cleanup inside the endpoint ownership lifetime. Keep the reproduction as regression coverage.
Alternatives rejected
Sleep longer in helpers: still schedule dependent and leaves production vulnerable.
Wait only for credential existence: another daemon can delete it afterwards.
Compare contents before deleting: a separate read/delete still races replacement.
Risk
Preserve the losing-start instance's guarantee that it never touches the winner's credential.
Keep graceful shutdown and startup-failure paths covered.
Activity
fix(agent-device): release the simlock lease when a command reports e…
V3RON commentedon Oct 16, 2026
Reproduction
Confirmed a deterministic credential-deletion race without hardware. The slow-lane failure itself was not rerun.
src/daemon/server.test.ts: “preserves the replacement daemon's admin credential when it starts as the old endpoint is released”. Two real endpoint hosts share credential storage. The replacement starts immediately after the old endpoint is released; its credential is verified before old shutdown resumes.Reproduction:
bug/609-repro, commitb92c940e(test only).Root cause
Defect.
src/daemon/server.ts:656–659releases the endpoint throughhost.stop()before removingadmin.token. A replacement can claim the released endpoint and persist its own credential while old shutdown is suspended; the old daemon then deletes the replacement's file. The helper's process-exit wait cannot guarantee protection:e2e/helpers/env.ts:288–301tolerates its timeout, and its PID lookup returns an empty list when unavailable. This confirms a production race consistent with the report, beyond the helper's existence check.Simplest fix
Remove the old credential before releasing the endpoint, keeping credential cleanup inside the endpoint ownership lifetime. Keep the reproduction as regression coverage.
Alternatives rejected
Risk
Written by an agent.
test: reproduce #609 credential deletion during daemon replacement
V3RON commentedon Oct 16, 2026
Delivery run
Merged: #625 (#609)
Parked: none
Waiting: none
Written by an agent.