Repository navigation
The roadmap's publish pass has never run: the credential the model carries, the units never supplied - #9501
Conversation
|
Execution receipt, at head 31/31, and the footer is quoted for the denominator rather than the verdict: an earlier dispatch of this same command passed Three things the run establishes beyond "it renders":
What this does not establish, stated because the PR title is about a pass that has never run: nothing here proves the publish pass works. It proves the units now carry the directive. The pass cannot be observed until an apply installs them, and #9489 records that the restored deploy mode should not be dispatched until the — sent from calm-ram-380 |
|
CI failure here is inherited from main, not from this diff, and I am not pushing a fix because there is nothing in this PR to fix.
What main is doing.
Why I am confident it is that and not mine, with a control rather than a plausible story. The floor produced none of its four artifacts ( What that does and does not establish. Identical annotations establish the same failure shape, not by themselves the same cause. Joined with this branch demonstrably carrying the same five-error breakage, and a diff that touches only
— sent from calm-ram-380 |
|
Upgrading the previous comment from inference to a read, since I flagged it as the former pending the log. The run has now completed, so the floor lane log is downloadable. It names the identical breakage: And the negative half, which is the part that actually clears this diff: grepping the full 1029-line log for
Confirmed rather than assumed. Worth noting for whoever picks up — sent from calm-ram-380 |
…tead Side-chat review ruled the previous cut grants far more than it names, and the ruling is right. WHY THE BINDING IS WITHDRAWN. EnvironmentFileIfPresent on both multipurpose units delivers the credential to the whole process AND every child. The belt spawns providers through `env -C <workdir> <program>` with neither -i nor -u, and GNU env inherits unless told otherwise, so the token reaches git, tmux and the dispatched provider session. Upstream makes it concrete: gh consults GH_TOKEN then GITHUB_TOKEN and those OVERRIDE credentials stored by `gh auth login` -- so an ambient publication token does not merely leak into a worker, it silently REPLACES the identity that worker was meant to act as. FILE-BACKED DELIVERY ALONE WOULD NOT HAVE FIXED IT, which is the tempting next move and is why it is written down. Moving the token from a variable to a file stops automatic inheritance and creates no authorization boundary: both units run as the deployment service user and the worker is spawned beneath that process, so a file readable by that principal is readable by the worker. The transport changes; the set of principals that can read it does not. TWO CREDENTIALS, TWO CAPABILITIES, DECLARED AND UNBUILT. Publication needs only to LIST pull requests (Pull requests: read). A worker needs to PUSH a branch and OPEN a pull request (Pull requests: write plus Contents). They are one token only by accident of both addressing GitHub. Neither is modeled today -- the dispatch platform declares a git workspace, tmux, environment binding and validation tools, and declares NO GitHub-authoring capability, no worker identity, and no credential source proving a worker can push. WHAT THIS PR NOW IS: the systemd optional-EnvironmentFile variant with its pair witness, and the instance-scoped credential path as a NAME with no consumer. Publication stays refused rather than restored by a grant nobody bounded. Restoring it needs a publication-only consumer boundary -- an uncredentialed belt handing a typed request to a minimal publisher the worker principal cannot read -- shared by both ingress paths. Stripping GH_TOKEN/GITHUB_TOKEN at the provider spawn is deliberately NOT the interim: with no worker credential modeled or observed, removing them could take away the only authentication a worker has and break its ability to offer finished work -- trading a scope defect for a capability loss. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two unit oracles asserted an EnvironmentFile line the previous commit removed, and their annotations described a deliberate divergence-until-next- apply that no longer exists. Both now agree with the live bytes again. The round trip is recorded rather than silently reverted, because the intermediate state was committed and a reader finding only the current text would not know why the credential line is absent. WHAT DID NOT CHANGE: publish_pass still refuses on every tick with "GITHUB_TOKEN is not set in the belt's environment". That refusal is now CORRECT rather than pending -- the belt is not supposed to hold that credential -- and it closes when the publication-only boundary lands, not when these units are next applied. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The roadmap's publish pass has never once run, and it is not a git-state defect
I went to fix R3 — "green is missing an
originremote" — and measured srv1 instead of trusting the note. The note was wrong, and correcting it is most of this PR's value.The belt is not stalled. It ticked at 18:13 today.
RemoteUrlInandLsRemoteHeadsboth succeed. Green is the retired slot (three stale Aug 1–2 worktrees); the live slot is/opt/gunbc/gunbcand it has a workingorigin. Nothing was ever blocked on a missing green remote.belt-last-tick.jsonsays exactly two things:and the publication receipt names the cause exactly:
The first two are revision drift — that's #9489's subject. This PR is the second.
The model was right; the deployment was incomplete
belt_publication_credentialdoes the correct thing and its own annotation explains why: the credential is carried as aCredentialSourceand probed for presence, so a read without a usable token is not attempted rather than sent as an empty bearer and raised as a 401. That is a well-formed fail-closed refusal, typed, located, and recorded.It refused on every tick because neither deployed unit ever supplied the variable. The graph carried a
CredentialSourcethe units could not satisfy. A usable credential has been on the host the whole time —ghis authenticated asbriansrlswithreposcope, which is also what makesls-remotework — it simply was not reachable as an env var.Two units reach that resolver, not one
The belt tick reaches it through
belt_publish_pass. The HTTP server reaches the same code throughPOST /publish/{node_id}→serve_publish_handler_response_for_instance→belt_publish_node_for_instance— the manual triggerserve_publish_notedescribes. Fixing only the belt would have left that route refusing for a reason its caller could not distinguish from the belt's.So: one instance-scoped file, read by both units. One credential belonging to one instance; two files would be the second representation §2 forbids, and would drift on rotation.
The optional form is the load-bearing choice
EnvironmentFilehad no optionality, so I addedEnvironmentFileIfPresentas a variant rather than arequired: Boolor a-smuggled into the path string — following this file's own precedent forEnvironmentvsEnvironmentFileandOnBootSecvsOnCalendar, and §4's dissolution ofidempotent: Boolinto a variant.Choosing the optional form is not laxity, and the annotation argues it: where the consumer adjudicates absence itself, the required form is strictly worse. A missing file would fail the unit, so the belt would never start and would write no receipt at all — a missing credential and a broken binary would render identically to every reader downstream. That is execution-provenance loss, bought in exchange for a strictness the consumer was already providing. The optional form preserves the typed, located, recorded refusal that already exists.
This is explicitly not the escape hatch
gunbc.host_disk_reclaim_unitsrefuses. That file would carry thresholds — host-local values overriding gates this repository owns. A file carrying a credential overrides no gate. The annotation states the distinction so the next reader picks by what the file carries rather than by which directive sounds stricter.The apply must not write this file
Unlike
gunbc-tree-sync.env, whose content is a runner path the deploy knows, a token is not derivable from anything in this graph and must not become one. The host owns the file; the graph owns only its name. A fresh instance whose file is absent gets a located refusal naming the path, rather than a belt that dies.Evidence
the_optional_environment_file_renders_systemds_dash_prefix— a new witness asserting both arms over one path. The inequality is the load-bearing half: the failure worth catching is not a wrong string but the two arms collapsing, which would silently make every required file optional.EnvironmentFile, which is the defect. Leaving them reading "as deployed on srv1" would have been a false claim in the row whose whole job is to be true.the_tree_sync_unit_matches_the_deployed_bytes_plus_the_exclusion_wideningis untouched and still passes — the required form is still reachable and still renders without the prefix.One out-of-band step remains, and it is not mine to take
Creating
/etc/gunbc-roadmap-credentials.env(0600) containing the token is a host credential write. I have not done it. It is a no-op until the units that read it are deployed, and deploying is itself blocked on #9489. Say the word and I will, or provision it yourself.What this does not claim
The publish pass is not verified working end to end. It cannot be until an apply installs these units, and #9489's own comment records that the restored deploy mode should not be dispatched until the
.gitrsync leg is replaced. What is verified is emission: the units now carry the directive, and the model can express the distinction it needs.