docs: the setup block is two commands, and the audit it needed is gone - #8
Merged
Conversation
OPERATIONS.md still told an operator to make the agent's state directory world-writable (`chmod 1777`) and then handed them forty lines of shell to work out whether that was exploitable on their machine. Neither is what the orchestrator prints any more. The agent now starts with `--user "$(id -u):$(id -g)"`, so it runs as the person who pasted the command rather than as the service account its image declares. Its state directory can therefore be `0700` and owned by them, nothing on the machine is shared with anybody, and the audit has nothing left to audit: every branch of it existed to make a world-writable parent safe, and it had to GUESS which uid the agent would run as (`a=10001`, printed before any agent existed) — a guess an operator can neither confirm nor act on. Its `UNSAFE` and `INCOMPLETE` verdicts went with it, so the paragraph that explained them is gone too. `--group-add` is unaffected, and the bullet now says why: it grants a supplementary GROUP, which is independent of the uid, so overriding the user does not take the socket away. A reader who does not know that assumes the two flags fight. What `--user` does not fix is stated rather than checked. `0700` protects the directory, not its NAME: an account that can write a directory ABOVE `$HOME/lspo-agent` can rename it away and leave its own under that name, the running agent never notices because its mount stays on the original inode, and the docker daemon resolves the replacement on the host and as root when it mounts a job's credentials. No checker for that ships anywhere — not in the printed block, not in the agent's log. Several shipped and were withdrawn, all wrong in the same family of ways, because a short check cannot soundly resolve a chain of symbolic links: canonicalising a path is exactly what discards the directory that HOLDS each link. The two sentences the orchestrator prints in their place are reproduced here word for word, so an operator reads the same thing in both places. The labels move with the facts. The `mkdir` line is BEHAVIOUR — skip it and the daemon creates the mount source as root, and the agent exits at startup with "Operation not permitted" — rather than the RECOMMENDATION it used to carry; and the gap is BEHAVIOUR for what nothing checks plus a RECOMMENDATION for what to do about it, never a RULE, since nothing refuses anything. Taken from `noderegistry/services.py::docker_run_command` at 7dcf95c5 rather than retyped: both the command and the two sentences were compared against that commit mechanically. `python -m pytest` is 128 passed, 1 skipped — the expected skip. Not touched, because it needs its own change: everything here that describes a job's credentials directory as `0700` with a `0600` file and tells a node author to build their image as uid 10001. The platform replaced that the day before (`0444` file inside a `0711` directory, re-applied on every write, precisely so an image running as its own user can read its own credentials), and this repository mentions neither mode. It spans PROTOCOL.md, AUTHORING.md, the uid-coupling section of OPERATIONS.md, the Dockerfile comment, `conformance/contract.py` and one platform test, so correcting half of it here would leave a document contradicting itself. Run with `LSPO_ORCHESTRATOR_SRC` set and the verbatim-citation check names it: `test_platform_rules.py:277` quotes a sentence `agent/creds.py` no longer contains. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The setup block in
docs/OPERATIONS.mdwas still the old shape:chmod 1777on theagent's state directory — world-writable, with the sticky bit — followed by roughly forty
lines of shell for the operator to run so they could find out whether that was exploitable
on their machine. The orchestrator prints two commands now, and nothing else.
What changed
chmod 1777→chmod 700.--user "$(id -u):$(id -g)"is the first flag of thedocker run. The agent runs asthe account that pasted the command rather than as the service account its image
declares, which is what makes
0700possible: its identity file and every running job'scredentials belong to that account.
The second fenced block — the
UNSAFE/INCOMPLETE/a=10001audit program — isdeleted, along with the paragraph that explained its verdicts. Every branch in it existed
to make a world-writable parent safe, and it had to guess which uid the agent would run
as, because it was printed before any agent existed.
The
--group-addbullet now says that it grants a supplementary group, which isindependent of the uid, so
--userdoes not take the socket away. Without that sentencea reader assumes the two flags fight.
What
--userdoes not fix is stated instead of checked, in the two sentences theorchestrator itself prints, word for word:
No checker for that ships anywhere — not in the block, not in the agent's log. Several
shipped and were withdrawn, all wrong in the same family of ways, because a short check
cannot soundly resolve a chain of symbolic links: canonicalising a path is exactly what
discards the directory that holds each link.
Labels
The
mkdirline is BEHAVIOUR (skip it and the docker daemon creates the mount sourceas root, and the agent exits at startup with "Operation not permitted"), not the
RECOMMENDATION it used to carry. The gap is BEHAVIOUR for what nothing checks plus
a RECOMMENDATION for what to do about it. Nothing here is a RULE: nothing in the
platform refuses anything on this path.
Provenance
Both the command and the two sentences were taken from
noderegistry/services.py::docker_run_commandin the orchestrator at7dcf95c5andcompared against that commit mechanically, not retyped. The doc's block is a strict subset
of what the generator prints; the one line it does not carry is
-e LSPO_AGENT_MAX_CONCURRENT_JOBS=1, which was already absent before this change.Tests
python -m pytest(this is CI, and it needs a real docker daemon): 128 passed, 1skipped — the expected skip is the verbatim-citation check, which needs a checkout of the
orchestrator.
What this does not fix, and why it is separate
Everything in this repository that describes a job's credentials directory as
0700witha
0600file, and tells a node author to build their image as uid 10001 to match theagent, rests on a premise the platform changed the day before this one: the credential file
is now
0444inside a0711directory, re-applied on every write, precisely so an imagerunning as its own user can read its own credentials. This repository mentions neither mode
anywhere.
Run the suite with
LSPO_ORCHESTRATOR_SRCpointed at an orchestrator checkout and theverbatim-citation check names it exactly:
That correction spans
docs/PROTOCOL.md,docs/AUTHORING.md, the uid coupling sectionof
docs/OPERATIONS.md, theDockerfilecomment,conformance/contract.py(
AGENT_UID,CREDENTIALS_DIR_MODE,CREDENTIALS_FILE_MODE),conformance/job.pyandone
subject_is_platformtest that measures the coupling with real containers. Correctinghalf of it inside this PR would leave a document that contradicts itself, so it is left
whole for its own change.
🤖 Generated with Claude Code