Skip to content

Two native-backend bugs from the live Lightsail launch have NixOS-side twins (#154 Phase 5) #356

Description

@defangdevs

The first live launch of the native Lightsail template (#154 Phase 4) turned up five bugs — all fixed in #355. Two of them are not purely native: the same logic exists on the NixOS side, where it is either latent or a duplication that already drifted. Filing separately so Phase 5's converge step has them written down, since #355 deliberately does not touch the module.

1. The ~/.config/agent-box tmpfiles rule is latent-broken on a fresh box

Both backends declare exactly one rule:

d /home/<user>/.config/agent-box 0700 <user> <user> - -

modules/agent-box.nix.in:2035 on the NixOS side; Renderer.tmpfiles on the native side. With no ~/.config yet, systemd-tmpfiles creates the parent itself, as root, then refuses to descend into it:

Detected unsafe path transition /home/agent (owned by agent)
  → /home/agent/.config (owned by root) during canonicalization
exit 73

~/.config/agent-box is then never created, and the supervisor's first act (mkdir ~/.config/agent-box) fails with Permission denied in a restart loop.

On a deployed NixOS box this does not bite, because ~/.config already exists user-owned by the time the rules run — on the box I checked it is drwxr-xr-x agent users, created by something else. That is luck, not design: the rule is one empty-home boot away from the same failure, and the VM tests do not cover a home with no .config.

Observed on Ubuntu 24.04, systemd 255. The NixOS box compared against runs systemd 258.

Fix: declare the parent, user-owned, in both backends. #355 does it for the native renderer:

d /home/<user>/.config 0755 <user> <user> - -
d /home/<user>/.config/agent-box 0700 <user> <user> - -

A test that starts from a home with no .config would be the thing that actually locks it.

2. The session seed exists twice, in two schemas

sessionsSeedFile (modules/agent-box.nix.in:342) and agentbox's user_seed both write the file modules/src/supervisor.sh reads. They disagreed: the native one emitted a list of {"name","agent"} where the supervisor reads {"version":1,"sessions":{"<name>":{…}}} with jq and expects every runtime field present. On the box that was

jq: error (at /home/agent/.config/agent-box/sessions.json:8):
    Cannot index array with string ("0")

every two seconds, with the seeded session listed as one named 0 that never started. #355 makes the native side emit the module's shape field for field, but two hand-written producers of one consumer's schema will drift again — and neither agentbox-render nor golden-snapshot compares them, because each checks its own backend's fixture in isolation.

Options, roughly in order of preference:

  1. A flake check that renders both backends from one spec and diffs the seed files (this is Phase 5's stated goal — the seed is the smallest useful first slice of it, and cheap: tests/native/expected/etc/agent-box/seed/agent-sessions.json and tests/golden/vm/payloads/agent-box-agent-sessions.json are already byte-comparable modulo formatting).
  2. Make the schema explicit somewhere both read — a modules/src/ JSON Schema, or a single generator both call — so the supervisor's expectations stop being folklore.
  3. Have the supervisor reject a seed it cannot parse loudly and once, instead of looping on a jq error every two seconds. Worth doing regardless of 1 and 2: the restart loop gave no hint that the shape was wrong.

Refs #154, #355, #325.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

Status
Backlog

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions