Skip to content

fix(ci): node infra results, sign-in audit rows, keyed pseudonyms, run identity (D-1198, D-1276, D-1044, D-1103) - #538

Merged
acamarata merged 9 commits into
mainfrom
dw10-plugins-22
Oct 29, 2026
Merged

acamarata merged 9 commits into
mainfrom
dw10-plugins-22

Conversation

@acamarata

Copy link
Copy Markdown
Contributor

Per row

D-1044 (identity part; the rest was already in main)

  • Already in main, unchanged: DeletePipelines prunes sched_admitted and sched_refusal (store/prune.go), the retryable to queued transition deletes the sched_admitted row (store/transitions.go), Coordinator.Run defers dispatchRelease (run/coordinator.go:210).
  • New: Options.User/Team -> Session.User/Team -> schedJob sets Job.User/Team. serve fills them from the run's intake row: user is the principal id of the stored submitter label (roles.PrincipalOf), team is the owner part of owner/repo. Decision: the coordinator has no team table, and the project owner is the organisation the repository belongs to. A run with no submitter (webhook) has no user and still gets the owner as team; empty values keep the old shared bucket.
  • Tests: TestSchedJobCarriesUserAndTeam (fails without the schedJob line), TestSubmitterIdentity.

D-1103

  • A node-reported infra result (error result or a check classed infra) and a cancel the run did not ask for now fail the lease to lost through the engine, so the CI-40 retry applies. An infra report counts against the node breaker, a node-chosen cancel does not. A cancel the run asked for still settles as cancelled. (run/remote_infra.go)
  • Signature: a result frame that carries a signature is verified by rebuilding the signed-remote document from the frame and running attest.RunnerVerifier (new attest.VerifyFrame); a refusal makes the attempt lost (infra, counts against the node). Decision on the signing policy: an unsigned frame is still accepted, because the P7-TRUST-20 agent contract lets a node without the signing-eligible marker complete unsigned and the release gate already refuses unsigned remote evidence. Mandatory signatures would exclude those nodes.
  • Tests: TestRemoteInfraResultTakesTheLostPath, TestRemoteResultSignatureMustVerifyWhenPresent (fails with the sink hook removed), TestVerifyFrame.

D-1198

  • Open: release files open with O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC and the mode is read from the descriptor (fstat). The operator trust file keeps following links but gets the same non-blocking open and fstat. Non-unix builds keep the Lstat check.
  • Pseudonym: runner-/coordinator- ids are an HMAC-SHA256 under a 32-byte operator key in <home>/pseudonym.key (0600, regular file, created atomically on first attempt, never published). Registered at attempt start; a bad key file fails the attempt instead of falling back to the unkeyed hash. With no key registered a Profile keeps the unkeyed form (the provenance golden files are unchanged). Decision: provisioning is local and automatic, so no operator action is needed.
  • Tests: TestOpenCheckedRefusesASwappedLeaf, TestOpenCheckedRefusesAFifoAtOpenTime, TestKeyedPseudonymsResistADictionary, TestLoadPseudonymKey*, TestAttemptStartRegistersThePseudonymKey.

D-1276

  • ActorFromPeer/ActorFromToken failures write a signin.fail row (actor token:unknown, token:<id>:<principal>, user:<id> or certificate:unknown; reason key and peer address, never the token). Rate limit: one row per peer and kind per 30 s, 20 rows per 30 s overall, and the next row carries suppressed=N. The user API passes the peer host via roles.WithPeer.
  • A refused serve admin mutation (validation or role) writes one <action>.refused row with the reason key.
  • Remote mutation rows: decided on the documented two-row pattern, not one combined row. The authorize row is the decision and exists even when no mutation follows (a denial); the mutation row is written first and fails closed. Recorded in roles/signin.go and the Trust wiki page, pinned by TestAuthorizeThenMutationLeavesTwoRows.
  • Tests: TestSignInFailuresWriteARateLimitedRow, TestSignInFailureBudgetBoundsManyPeers, TestSignInFailureRowsForKnownTokensAndCertificates, TestRefusedAdminMutationsWriteARow.

Checks (Go 1.26, darwin/arm64, from free/ci, -mod=vendor)

  • go vet ./... clean; gofmt clean on touched files.
  • go test -count=1 ./...: all pass except cmd/agent TestServeStdioExitsNonZeroWithE663OnAProdStackMarker and internal/legacy TestSecretsEnvLiveKeyGateTrackedValueOnly. Both fail the same way on a clean origin/main worktree (pre-existing).
  • An earlier full run caught a race in key creation (concurrent attempts read a half-written key); fixed with a temp file plus link and covered by TestLoadPseudonymKeyConcurrentFirstUse.
  • PR fix(ci): close run lifecycle debt (D-1309, D-0998, D-1036, D-0970, D-1103, D-1017) #512 lists D-1103 as blocked with no code change; this PR does not overlap its code.

@acamarata

Copy link
Copy Markdown
Contributor Author

Review fix 0993d8f: publish.build now registers the CI home's pseudonym key (attest.UsePseudonymKeyAt) before it builds the provenance builder id and the manifest coordinator id, so nself ci artifacts sign and the run's release publisher publish the same keyed ids; an unusable key refuses the publication instead of falling back to the unkeyed hash. New test TestPublishOutsideARunUsesTheKeyedPseudonyms fails on the previous head (no key created, unkeyed ids) and passes now. provenance.BuildAgent only uses its profile to scrub file names and publishes no pseudonym, so it needs no key. Wiki sentence updated. Module vet and tests pass.

@acamarata

Copy link
Copy Markdown
Contributor Author

Rebased onto origin/main and resolved the conflict in .github/wiki/guides/ci/Trust-Security-and-Privacy.md by keeping both sides: the expanded audit-row documentation for refused sign-ins and admin mutations, plus the TEAM self_approval clarification. All CI module tests pass with -mod=vendor (exit 0). Force-pushed to dw10-plugins-22 at 6b2b2c3.

@acamarata
acamarata merged commit 646bb67 into main Oct 29, 2026
27 of 32 checks passed
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