fix(credentials): gitea arms resolve seat slots — fail loud, no silent service fallback #1370

Open
code-infra-01 wants to merge 1 commits from fix/credentials-gitea-seat-slots into next
Member

From the 2026-08-22 credential-outage thread (stack#1368 and fred's blast-radius tasking): load_credentials gitea-mosaicstack / gitea-usc resolve only the shared service store, while the live credential path on a fleet host is the seat slot. The git credential helper and detect-platform's get_gitea_token already resolve seat slots; this brings the one remaining gitea consumer to the same rule.

Behavior

  • MOSAIC_GIT_IDENTITY naming a seat (directory under ${MOSAIC_BRAIN_HOME:-~/.mosaic}/fleet/agents/<identity>/): token from <slot>/secrets/gitea-<instance>-<identity>.token, exported as GITEA_TOKEN. URL still from credentials.json — it is provider config, not identity.
  • Seat with an empty/missing slot: rc 1 refusal naming the identity and the slot path. NO fallback to the service store — the no-fallback rule the helper and get_gitea_token already enforce; a silent fallback acts as the wrong identity (#1343 family; usc/uconnect#3084 precedent).
  • No identity, or a non-seat identity: service store, byte-for-byte unchanged behavior.
  • Other services untouched: woodpecker/authentik/glpi have no seat concept, and inventing one there would be speculative.

Scoping (blast radius, measured before writing)

56 load_credentials call sites, 40 files, 9 suites; the gitea arms are the only services with per-seat credentials. One edit in the library, not 56 at call sites.

Test

test-credentials-gitea-seats.sh, hermetic (sandbox brain + sandbox credentials.json; no real credential read), CI-reachable via test:framework-shell. Six pins: G1 slot-sourced token; G2 seat-miss refusal WITHOUT fallback (asserts the token does not equal the service-store value — the discriminating half); G3 no-identity service path; G4 non-seat identity; G5 woodpecker indifferent to the identity; G6 pre-set GITEA_TOKEN never overridden.

Mutant kills (both measured): seat-miss-falls-back → dies at G2; seat-reads-service-store → dies at G1.

Known pre-existing, out of scope

This host's credentials.json stores gitea tokens under .gitea.<inst>.default and per-identity keys while the loader reads .gitea.<inst>.token; the no-identity service path fails identically before and after this change (verified against origin/next's loader on the same file). Flagged for the wrapper-defect batch rather than widened here.

From the 2026-08-22 credential-outage thread (stack#1368 and fred's blast-radius tasking): `load_credentials` gitea-mosaicstack / gitea-usc resolve only the shared service store, while the live credential path on a fleet host is the seat slot. The git credential helper and detect-platform's get_gitea_token already resolve seat slots; this brings the one remaining gitea consumer to the same rule. ## Behavior - `MOSAIC_GIT_IDENTITY` naming a seat (directory under `${MOSAIC_BRAIN_HOME:-~/.mosaic}/fleet/agents/<identity>/`): token from `<slot>/secrets/gitea-<instance>-<identity>.token`, exported as `GITEA_TOKEN`. URL still from credentials.json — it is provider config, not identity. - Seat with an empty/missing slot: rc 1 refusal naming the identity and the slot path. NO fallback to the service store — the no-fallback rule the helper and get_gitea_token already enforce; a silent fallback acts as the wrong identity (#1343 family; usc/uconnect#3084 precedent). - No identity, or a non-seat identity: service store, byte-for-byte unchanged behavior. - Other services untouched: woodpecker/authentik/glpi have no seat concept, and inventing one there would be speculative. ## Scoping (blast radius, measured before writing) 56 `load_credentials` call sites, 40 files, 9 suites; the gitea arms are the only services with per-seat credentials. One edit in the library, not 56 at call sites. ## Test `test-credentials-gitea-seats.sh`, hermetic (sandbox brain + sandbox credentials.json; no real credential read), CI-reachable via `test:framework-shell`. Six pins: G1 slot-sourced token; G2 seat-miss refusal WITHOUT fallback (asserts the token does not equal the service-store value — the discriminating half); G3 no-identity service path; G4 non-seat identity; G5 woodpecker indifferent to the identity; G6 pre-set GITEA_TOKEN never overridden. Mutant kills (both measured): seat-miss-falls-back → dies at G2; seat-reads-service-store → dies at G1. ## Known pre-existing, out of scope This host's `credentials.json` stores gitea tokens under `.gitea.<inst>.default` and per-identity keys while the loader reads `.gitea.<inst>.token`; the no-identity service path fails identically before and after this change (verified against origin/next's loader on the same file). Flagged for the wrapper-defect batch rather than widened here.
code-infra-01 added 1 commit 2026-08-22 05:24:23 +00:00
On a fleet host, load_credentials gitea-mosaicstack / gitea-usc now resolve
a git identity's SEAT SLOT (MOSAIC_GIT_IDENTITY with a directory under
MOSAIC_BRAIN_HOME/fleet/agents/) as the token source, mirroring
get_gitea_token in detect-platform.sh (the #1311 lineage). URL still comes
from credentials.json (provider config, not identity). A seat with an empty
or missing slot REFUSES (rc 1, identity and slot path named) rather than
falling back to the shared service store — the same no-fallback rule the
git credential helper and detect-platform enforce; a silent fallback would
act as the wrong identity (#1343 family, usc/uconnect#3084 precedent).
No identity resolved, or a non-seat identity: the service store, unchanged.
Other services untouched (no seat concept to invent).

Hermetic suite test-credentials-gitea-seats.sh (sandbox brain + sandbox
credentials.json; no real credential read), CI-reachable via
test:framework-shell. Six pins: slot-sourced token, seat-miss refusal
without fallback (token compared against the service value), no-identity
service path, non-seat identity, woodpecker indifference to the identity,
pre-set GITEA_TOKEN never overridden. Mutants killed: seat-miss-falls-back
(dies at G2), seat-reads-service-store (dies at G1).

Known pre-existing (out of scope, unchanged by this PR): this host's
credentials.json stores gitea tokens under .gitea.<inst>.default and
per-identity keys, while the loader reads .gitea.<inst>.token — the
no-identity service path fails identically before and after this change
(verified against origin/next's loader). Filed for the wrapper-defect
batch rather than widened here.
code-infra-01 requested review from rev-code-02 2026-08-22 05:24:31 +00:00
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
Required
Details
This pull request has changes conflicting with the target branch.
  • packages/mosaic/package.json
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/credentials-gitea-seat-slots:fix/credentials-gitea-seat-slots
git checkout fix/credentials-gitea-seat-slots
Sign in to join this conversation.