Skip to content

fix(node-sync): materialize the git identity into the context mount, not the overlay home#552

Merged
gbasin merged 1 commit into
masterfrom
fix/identity-context-mount
Jul 16, 2026
Merged

fix(node-sync): materialize the git identity into the context mount, not the overlay home#552
gbasin merged 1 commit into
masterfrom
fix/identity-context-mount

Conversation

@gbasin

@gbasin gbasin commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

What

Agent commits still landed as Centaur AI after #546 — with the identity file sitting on disk, readable from the node. This moves the [include] target from the agent's overlay home onto the context mount (~/context/.atrium-git-identity).

Why the overlay home can never work

The include mechanism poisons itself, and the ordering is not a race — it's guaranteed:

entrypoint.sh runs git config at pod creation. git reads /opt/centaur/gitconfig, resolves the [include] target, gets ENOENT, and the kernel caches a negative dentry — minutes before a claim exists to say who the user is. By the time node-sync writes the identity, the lookup has already missed. The only question is whether that negative entry gets invalidated.

Overlay mount instances keep independent dentry trees, so a node-side create never invalidates the pod's entry — neither writing into upper (shipped, failed) nor through merged (#546, failed). The forensic signature is readdir listing the file while lookup returns ENOENT.

A bind mount of a single ext4 superblock shares one dentry tree, so the create is observed even after a failed lookup. ~/context is exactly that (hostPath /var/lib/centaur/atrium/<session>, readOnly on the pod side — the agent can read its identity but can't clobber it).

Measured on a live prod pod, both ways, before this was written.

Verification

Negative control in a privileged container with a real-fs upperdir — proves the test discriminates rather than passing vacuously:

topology pod lookup after node-side create
overlay, two instances, shared upper (#546) BLIND — readdir shows it, lookup ENOENT
overlay, node writes straight to upper BLIND
bind mount, one ext4 superblock (this PR) SEES IT

Privileged Docker gate green: cargo fmt --check, cargo clippy --all-targets -D warnings, full cargo test (27 tests) including the new git_identity_visibility running as root (not skipped).

Prod confirmed on CENTAUR_SANDBOX_OVERLAY_FLAT_HOME=true, so ~/context is the context mount path.

Notes

  • The new test asserts the ordering that matters: mount, MISS, write, hit. The fix(node-sync): write the git identity through the merged view, not the overlay upper #546 test wrote before looking up, so it passed on the broken design — that's how this shipped twice.
  • CI's cargo test lane is unprivileged and reports this file as "ok" while skipping every case; the privileged step (renamed here) is what actually runs it.
  • Failures still never gate the mount — an absent identity degrades to the baked Centaur AI, i.e. pre-existing behavior.

…not the overlay home

The agent still committed as "Centaur AI" after #546, with the identity file
plainly on disk. The include mechanism poisons itself:

  entrypoint.sh runs `git config` at POD CREATION. git reads the global config,
  resolves the [include] target, gets ENOENT, and the kernel caches a NEGATIVE
  dentry for that path -- minutes before any claim exists. So the lookup has
  ALWAYS already missed by the time node-sync learns who the user is, and the
  only question is whether that negative entry gets invalidated.

Overlay mount instances keep independent dentry trees, so a node-side create
never invalidates the pod's entry -- into `upper` (shipped, failed) or through
`merged` (#546, also failed). Forensic signature: readdir lists the file while
lookup returns ENOENT.

A bind mount of one ext4 superblock shares the dentry tree, so the create IS
observed after a failed lookup. `~/context` is exactly that. Move the include
target there. Measured both ways on a live prod pod before writing this.

- Dockerfile: include.path -> /home/agent/context/.atrium-git-identity
- node-sync: write into scoped_atrium_root (the context root), not the overlay
- tests: assert through a bind mount AFTER a negative lookup -- the shape of the
  actual bug. The old test wrote before looking up, so it passed on the broken
  design too; that is how this shipped twice.
- CONTRACT.md: record why, so it does not get tidied back into the home
@gbasin
gbasin added this pull request to the merge queue Jul 16, 2026
Merged via the queue into master with commit 92b8520 Jul 16, 2026
16 checks passed
@gbasin
gbasin deleted the fix/identity-context-mount branch July 16, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant