SECURITY: get_gitea_token fails OPEN on unset identity (silent shared-credential fallback) — opposite to git-credential-mosaic's fail-closed; fleet seat silently authors API writes as shared owner #1044
Open
opened 2026-08-04 03:11:47 +00:00 by Ghost
·
4 comments
No Branch/Tag Specified
next
refactor
fix/1257-adopt-draft-transition
docs/prd-rev1-ratification
r4-helper-port
docs/containerization-plan
feat/m4-4b-enrollment-command
feat/m4-4a-enrollment-schema
feat/m4-4-0-enrollment-design
feat/m4-3a-p1-stop-mission-task-status-writes
docs/m4-3a0-p0-map-currency
docs/c2-amendment1-company-crud
config/minimal-subset
feat/m4-1b-ii-hierarchy-commands
mosaic-cli-p1-wrappers
mosaic-cli-p1-dispatch
docs/ruling-4b-company-visibility
feat/m4-1b-hierarchy-gateway
feat/m4-1a-hierarchy-schema
feat/p6-e2e-ci-gate
feat/p5-spa-cutover
fix/1451-appservice-dockerfile-scripts
contract/onboarding-wizard
contract/custody-schema
contract/api-artifacts
fix/appservice-dockerfile-scripts
docs/t78-cli-capability-migration
contract/rollup-projection
contract/hierarchy-schema
fix/invariant-r-version-probe-retry
contract/mode-conversion
contract/tool-gateway-mapping
contract/rbac-grants
contract/identity-lifecycle
chore/s1-docs-hygiene
docs/ri-050-release-evidence
feat/webui-p4-2-settings-admin
fix/bootstrap-race
fix/teams-enumeration-scope
fix/1407-next-image-parity
docs/prd-north-star-rewrite
rescue/ms-gate-001-gatekeeper
fix/1394-recover-token-headless
fix/1390-uninstall-headless
fix/1403-n1n2-followup
fix/1391-validationpipe-boot-check
archive/salvage-20260825/wp5b-consumer-compat
wp5b-consumer-compat-2
archive/salvage-20260825/t63-fix-2648
archive/salvage-20260825/t63-fix-1389
archive/salvage-20260825/i1380ff-fix
i1380-guard
fix/send-message-exact-target-pin
t51p2wp0b
archive/ms24-fork
fix/ci-queue-wait-no-ci-merge-path
fix/credentials-gitea-seat-slots
feat/onboarding-scripts-framework
pr-1367
fix/1357-issue-view-comments
fix/1356-tea-login-fail-closed
fix/1362-harness-aware-delivery-confirm
fix/gitea-guessed-login-credential
docs/w4-document-contract
fix/d29-lease-revoke-noop
peggy/agent-send-unverified-label
fix/pr-merge-fork-ci-status
riv001-clean
docs/1216-trunk-parameterization
fix/1256-fleet-pane-path-node
fix/1017-enumeration-guard-population
fix/1182-fail-closed-launch
fix/1327-setuppath-idempotency
merge/main-into-next
ci/push-ci-comment-model
ci/pin-ci-base-image
fix/ci-queue-wait-no-status
fred/code-review-pinned-tool-rules
fred/guides-seat-identity-fleet-comms
fred/credential-fail-closed-seat-slots
fix/fleet-greenfield-blockers
feat/ri-050-qr-evaluator
archive/salvage-20260825/zane/doctor-greenfield-hint
archive/salvage-20260825/fix/ri-050-registry-secrets
archive/salvage-20260825/docs/ri-050-release-evidence
docs/ri-050-forge-docs-fastfollow
fix/ri-050-registry-secrets
test/ri-050-publish-gate-negative
archive/salvage-20260825/fix/ri-050-verify-pglite-path
fix/ri-050-verify-pglite-path
docs/ri-050-qr-probe-inventory
archive/salvage-20260825/zane/doctor-brain-home
feat/ri-050-web-stale-safety
archive/salvage-20260825/pr-1298
archive/salvage-20260825/zane/mosaic-home-support
docs/ri-050-mission-bootstrap
fix/ri-050-forge-fail-closed
feat/ri-050-publish-gate
fleet/continuation-record-2026-08-17
feat/ri-050-prd-authority
fix/ri-050-macp-fail-closed
fix/1280-identity-first-resolution
feat/w-f4-store
fix/1264-fleet-unattended-first-start
fix/1269-ci-chain-unblock
fix/1256-fleet-runtime-preflight
fix/1257-e7-draft-transition
fix/1240-fleet-transport-check
fix/1017-wire-start-agent-session
e2e-compose
fix/1241-launch-failure-visible
fix/1237-fleet-v2-dispatch
fix/1236-installer-dir-modes
fix/installer-path-and-node
feat/wf-fleet-mvp
fix/installer-provisions-node
fix/lease-test-env-isolation
release/0.0.50-integration
feat/wf5-main-merge
feat/wf5-securestorage
feat/1216-trunk-resolver
docs/1214-branch-process
docs/ia-merge-current
fix/869-lease-probe-timeout
main
feat/workspace-hygiene-tool-enforcement
feat/1080-pr-edit
fix/1179-required-security-di
feat/p3-slice0-task5-chat-runtime-router-shaggy
feat/p3-slice0-task5-chat-runtime-router
feat/wf1-composition
feat/p3-slice0-task4-web-catalog-selection
feat/lease-promotion-and-harness-isolation
ci/provision-pi-runtime
feat/p3-slice0-task3-catalog-selection
feat/p3-slice0-task2-harness-registry
adopt/965-mos-ste-writing-standard
fix/991-comment-url-scheme-normalise
feat/wf2-bundle-migration
feat/wf4-plugin-acquisition
feat/wf5-refresh-safety
fix/1145-coord-di-compiled-boot
feat/p3-slice0-task1-harness-contracts
docs/webui-phase-p-structure
feat/1150-pi-goal-extension
feat/webui-p3-chat
fix/1146-ci-queue-purpose
fix/1138-conditional-federation
feat/webui-p2-data-auth
fix/gateway-runner-image
feat/webui-p1-vite-skeleton
fix/break-c-hooks-and-web-image
docs/webui-fleet-claude-bridge-plan
fix/wizard-gateway-failure
fix/next-node-gate
fix/mosaic-init-rce
greenfield/fomo-lin
fix/1099-pipefail-wake
fix/1099-pipefail-tests
fix/1099-pipefail-sweep
fix/framework-shell-portability
fix/1043-pane-git-identity
fix/1081-issue-close-silent-comment-failure
fix/1090-enrollment-wallclock-tolerance
feat/1082-tea-stale-token-diagnostic
fix/detect-platform-silent-128-outside-repo
feat/1050-install-state-machine-red-fixture
fix/pr-merge-message-field
feat/1051-mosaic-brain-installer
feat/1045-mosaic-cred
remediation/state
fix/1056-upgrade-rollback-control-race
fix/1019-ci-queue-timeout-harness
feat/rm-02-gate-registry
fix/rm-01-reproducible-checkout
remediation/mission-setup
fix/hygiene-inert-format-gate
fix/1019-queue-guard-stdin
feat/mos-ste-writing-standard
fix/1017-enumeration-guard
fix/1007-suite-hermeticity
feat/push-guard-null-case-verification
feat/wake-preimage-provenance
mos-comms-live
docs/heartbeat-framework-layering-ms-lead
feat/869-c4-version-coupling
feat/869-c2-install-ordering-guard
feat/869-c5-doctor-activation-check
feat/per-agent-gitea-identity
fix/875-belongs-case-insensitive-slug
fix/ci-queue-wait-404-branch-absent
feat/869-c1-activation-probe
feat/869-c3-broker-supervisor
fix/865-tea-cli-comment-invocation
feat/glpi-skills
fix/860-deflake-mutator-lease-gate
fix/850-detect-platform-port-normalization
fix/856-worktree-deps-preflight
fix/835-pr-review-approve-reject-comment-flag
fix/848-truthful-evidence
fix/812-pr-review-comment
fix/849-recovery-runtime-fixture-race
docs/758-ledger-m5-001-sync
feat/834-tc-server-side-doc
feat/833-constrained-recovery-command
feat/827-gate0-probe
governance/gate0-probe3-amendment
fix/795-codex-pr-diff
fix/795-ci-base-jq
fix/795-ci-base-git
feat/791-pr3-fleet-regen
feat/791-pr2-snapshot-restore
fix/807-glpi-206
fix/808-agent-send-false-sender
feat/791-upgrade-config-protection
feat/790-mosaic-yolo-claudex-pr2
feat/790-mosaic-yolo-claudex
feat/758-v1-v2-migrator
fix/766-exact-fleet-comms
test/758-reconciler-lifecycle-gates
docs/771-kbn101-db-role-split
test/758-example-profile-dispositions
feat/758-shared-role-resolution
feat/mos-logical-identity-fencing
feat/769-kbn100-unified-schema
docs/753-kbn010-threat-gate
feat/758-roster-v2-compiler
feat/756-official-discord-plugin
fix/mos-option2-qualification-format
docs/issue-758-m0
docs/mos-option2-qualification
mos-comms
feat/tess-interaction-agent
fix/tess-docs-format
draft/mosaic-platform-prd
fix/installer-provider-gate-and-local-gateway-redis
release/mosaic-cli-0.0.37
feat/framework-constitution-alpha
fix/git-wrapper-repo-detection
fix/woodpecker-wrapper-legacy-mosaic
fix/t-a292e96f-gitea-pr-metadata
fix/gitea-pr-metadata-login-t-a292e96f
fix/t_a292e96f-pr-metadata-gitea
fix/t_3a368a52-gitea-usc-login
fix/bootstrap-hotfix
fix/populate-known-packages-list
fix/idempotent-init
archive/salvage-20260825/fix/ci-prisma-generate
archive/salvage-20260825/feat/ms-gate-001-gatekeeper-local
archive/salvage-20260825/feat/ms-gate-001-gatekeeper
archive/salvage-20260825/feat/ms24-ci-webhook
archive/salvage-20260825/fix/mission-control-proxy-routes
archive/salvage-20260825/fix/deploy-missing-env-and-networks
archive/salvage-20260825/fix/mission-control-query-provider
archive/salvage-20260825/test/ms23-p2
archive/salvage-20260825/feat/ms23-p2-audit
archive/salvage-20260825/feat/ms23-p2-roster
archive/salvage-20260825/feat/ms23-p1-proxy
archive/salvage-20260825/feat/ms23-p1-registry
archive/salvage-20260825/feat/ms23-p1-internal-provider
archive/salvage-20260825/feat/ms23-p1-interface
archive/salvage-20260825/chore/ms23-tasks-p0-complete
archive/salvage-20260825/test/ms23-p0
archive/salvage-20260825/chore/ms23-tasks-p005-006
archive/salvage-20260825/feat/ms23-p0-tree
archive/salvage-20260825/chore/ms23-tasks-p004-005
archive/salvage-20260825/feat/ms23-p0-controls
archive/salvage-20260825/chore/ms23-tasks-p0-002-004
archive/salvage-20260825/feat/ms23-p0-stream
archive/salvage-20260825/fix/ms23-prisma-rm-symlink
archive/salvage-20260825/fix/ms23-prisma-kaniko-symlink
archive/salvage-20260825/fix/ms23-prisma-script-path
archive/salvage-20260825/fix/ms23-prisma-docker-vs-ci
archive/salvage-20260825/fix/ms23-prisma-schema-local
archive/salvage-20260825/fix/ms23-prisma-api-pkg
archive/salvage-20260825/fix/ms23-prisma-cli
archive/salvage-20260825/fix/ms23-orchestrator-prisma-generate
archive/salvage-20260825/feat/ms23-p0-ingestion
archive/salvage-20260825/feat/ms23-p0-schema
archive/salvage-20260825/fix/agent-template-auth-module
archive/salvage-20260825/feat/ms22-p2-discord-router
archive/salvage-20260825/test/ms22-p2-agent-tests
archive/salvage-20260825/chore/ms22-p2-docs-update
archive/salvage-20260825/feat/ms22-p2-agent-routing
archive/salvage-20260825/chore/ms22-p2-update-docs
archive/salvage-20260825/feat/ms22-p2-user-agents
archive/salvage-20260825/feat/ms22-p2-agent-crud
archive/salvage-20260825/fix/security-audit-multer
archive/salvage-20260825/ci/portainer-deploy
archive/salvage-20260825/fix/ms21-missing-user-auth-migration
archive/salvage-20260825/infra/fix-mosaic-db-init-extensions
archive/salvage-20260825/infra/migrate-to-openbrain-db
archive/salvage-20260825/fix/flaky-queue-test
archive/salvage-20260825/fix/deploy-service-names
archive/salvage-20260825/fix/deploy-service-update
archive/salvage-20260825/fix/deploy-user-v2
archive/salvage-20260825/fix/deploy-user
archive/salvage-20260825/fix/orchestrator-widget-endpoints
archive/salvage-20260825/fix/dashboard-widget-mock-data
archive/salvage-20260825/fix/ci-glibc-image
archive/salvage-20260825/fix/dockerfile-npmrc
archive/salvage-20260825/fix/matrix-native-binary
archive/salvage-20260825/fix/kaniko-cache
archive/salvage-20260825/fix/base-image-kaniko-v2
archive/salvage-20260825/fix/base-image-kaniko
archive/salvage-20260825/feat/custom-base-image
archive/salvage-20260825/ci/pnpm-cache
archive/salvage-20260825/fix/interceptor-tests
archive/salvage-20260825/fix/kanban-tests
archive/salvage-20260825/feat/wire-chat
archive/salvage-20260825/feat/usage-widget
archive/salvage-20260825/feat/usage-widget-review
archive/salvage-20260825/fix/security-hardening
archive/salvage-20260825/fix/project-domain-attach
archive/salvage-20260825/fix/project-domain-v2
archive/salvage-20260825/feat/kanban-add-task
archive/salvage-20260825/fix/logs-page-clean
archive/salvage-20260825/fix/logs-page
archive/salvage-20260825/fix/workspace-members
archive/salvage-20260825/fix/ci-lint-632
archive/salvage-20260825/fix/lint-from-632
archive/salvage-20260825/fix/file-manager-tags
archive/salvage-20260825/fix/csrf-debug-log
archive/salvage-20260825/fix/controller-type-imports
archive/salvage-20260825/fix/system-admin-env
archive/salvage-20260825/fix/gateway-cors-trusted-origins
archive/salvage-20260825/fix/fleet-provider-form-dto-v2
archive/salvage-20260825/fix/ms22-audit
archive/salvage-20260825/fix/orchestrator-widgets
archive/salvage-20260825/fix/fleet-provider-form-dto
archive/salvage-20260825/fix/orchestrator-widgets-preexisting
archive/salvage-20260825/fix/csrf-bearer-bypass
archive/salvage-20260825/fix/ms22-missing-authmodule-imports
archive/salvage-20260825/fix/container-lifecycle-config-module
archive/salvage-20260825/fix/swarm-compose-ms22-vars
archive/salvage-20260825/chore/ms22-p1-complete
archive/salvage-20260825/feat/ms22-p1k-idle-reaper
archive/salvage-20260825/feat/ms22-p1j-docker
archive/salvage-20260825/feat/ms22-p1e-onboarding-api-work
archive/salvage-20260825/feat/ms22-p1c-config-api
archive/salvage-20260825/chore/ms22-prd-tracking
archive/salvage-20260825/feat/ms22-p1b-crypto
archive/salvage-20260825/docs/ms22-architecture
archive/salvage-20260825/feat/ms22-openclaw-docker
archive/salvage-20260825/feat/ms22-openclaw-gateway-module
archive/salvage-20260825/chore/ms21-complete
archive/salvage-20260825/chore/ms21-final-tasks-done
archive/salvage-20260825/fix/ms21-ui-001-qa
archive/salvage-20260825/feat/ms22-openclaw-docker-backup-20260301
archive/salvage-20260825/chore/ms22-phase0-complete
archive/salvage-20260825/feat/ms21-ui-teams-rbac-v3
archive/salvage-20260825/test/ms22-integration
archive/salvage-20260825/feat/ms22-ingest-clean
archive/salvage-20260825/feat/ms21-ui-users-members
archive/salvage-20260825/feat/ms22-ingest
archive/salvage-20260825/feat/ms22-task-agent
archive/salvage-20260825/chore/ms22-tasks-tracking
archive/salvage-20260825/feat/ms21-ui-teams-rbac
archive/salvage-20260825/fix/openbao-otel-cve
archive/salvage-20260825/ci/unified-pipeline
archive/salvage-20260825/feat/ms22-conversation-archive
archive/salvage-20260825/feat/ms22-agent-memory
archive/salvage-20260825/feat/ms22-findings
archive/salvage-20260825/feat/ms22-knowledge-schema
archive/salvage-20260825/chore/tasks-final
archive/salvage-20260825/chore/tasks-update
archive/salvage-20260825/feat/ms21-session-invalidation
archive/salvage-20260825/feat/ms21-rbac-settings
archive/salvage-20260825/feat/ms21-rbac
archive/salvage-20260825/feat/ms21-ui-user-dialogs
archive/salvage-20260825/feat/ms21-ui-workspace-members
archive/salvage-20260825/feat/ms21-ui-teams
archive/salvage-20260825/chore/ms21-tasks-ui-progress
archive/salvage-20260825/feat/ms21-ui-workspaces
archive/salvage-20260825/feat/ms21-ui-users
archive/salvage-20260825/chore/ms21-tasks-schema-fix
archive/salvage-20260825/feat/ms21-import-api
archive/salvage-20260825/test/ms21-migration-tests
archive/salvage-20260825/feat/ms21-teams-page
archive/salvage-20260825/feat/ms21-users-page
archive/salvage-20260825/chore/ms21-task-update-p1-p3
archive/salvage-20260825/feat/ms21-admin-module
archive/salvage-20260825/fix/websocket-reconnect
archive/salvage-20260825/merge/develop-to-main
skill-lifecycle-v1
onboarding-v1
agent-seats-v1
interactive-agent-v1
auto-apply-v1
session-fork-v1
retention-v1
mission-policy-v1
conductor-v1
workspace-capabilities-v1
sessions-v1
operator-ergonomics-v1
adapter-seam-v1
release-model-v1
mission-task-v1
config-hello-v1
poc-container-hello-v0
v0.0.39-alpha
mosaic-v0.0.31
fed-v0.2.0-m2
fed-v0.1.0-m1
mosaic-v0.0.29
mosaic-v0.0.28
mosaic-v0.0.27
mosaic-v0.0.26
mosaic-v0.0.25
mosaic-v0.0.24
v0.2.0
v0.1.0
v0.0.8
v0.0.7
v0.0.6
v0.0.5
v0.0.4
archive/ms24-fork-20260823
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Assignees
code-be-01 (Mosaic fleet seat code-be-01)
code-be-02 (Mosaic fleet seat code-be-02)
code-dogfood-01 (Mosaic fleet seat code-dogfood-01)
code-infra-01 (Mosaic fleet seat code-infra-01)
darkwing (Mosaic fleet seat darkwing)
dewey (Mosaic fleet seat dewey)
fargo
filbert (Mosaic fleet seat filbert)
fred
gate-merge-01 (Mosaic fleet seat gate-merge-01)
happy
jason.woltje (Jason Woltje)
marcie
merge-gate
ops-01 (Mosaic fleet seat ops-01)
ops-02 (Mosaic fleet seat ops-02)
ops-03 (Mosaic fleet seat ops-03)
ops-ci-01 (Mosaic fleet seat ops-ci-01)
ops-deploy-01 (Mosaic fleet seat ops-deploy-01)
orch-01 (Mosaic fleet seat orch-01)
pepper
resume
rev-code-01
rev-code-02
rev-security-01
rev-security-02
rev-security-03 (Mosaic fleet seat rev-security-03)
rocko (Mosaic fleet seat rocko)
sanity
scooby (Scooby)
scrappy
shaggy
tiny
topher (Mosaic fleet seat topher)
velma
veronica (Mosaic fleet seat veronica)
vision
woodpecker
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mosaicstack/stack#1044
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary (SECURITY)
The two identity-resolution paths diverge on the unset-identity case, and they fail in opposite directions:
git-credential-mosaic(git push/fetch) — identity unset → FAILS CLOSED (refuses, escalatesreason=no-identity). Loud and safe.get_gitea_token(framework/tools/git/detect-platform.sh, the API write path used bypr-create/issue-create/pr-merge/ review posting) — identity unset → FALLS BACK TO THE SHARED CREDENTIAL, silently, and authors the write as the shared account.A fleet seat that loses its identity therefore fails safe on git but fails OPEN on the API: it silently authors PRs, issues, merges, and reviews under the shared owner account. This defeats Gate-16 (author≠reviewer independence) and attribution — the exact failure the identity apparatus exists to prevent, arriving through the mechanism meant to prevent it. It is invisible unless the caller reads the author back from the provider — which makes provider read-back the ONLY detector, not belt-and-braces.
Confirmed by read
detect-platform.shget_gitea_token():return 1, "Refusing to borrow another slot's token") is reachable only when_identis non-empty (identity SET but no per-slot token for the host)._identis empty (identity lost / never set), the entire per-identity block is skipped and execution falls through to the shared credential loader (step 1:load_credentials gitea-usc/gitea-mosaicstack). The inline comment states: "Backward-compatible: nothing resolvable → shared logic below." That backward-compat is the hole for fleet seats.Failure-direction matrix
return 1)How a seat loses its identity (the trigger is real, not hypothetical)
2026-08-03 incident: fleet kickstarts set identity via a session-start
export MOSAIC_GIT_IDENTITY=..., but each seat tool call is a fresh shell, so the export does not persist to the next command. Merges that worked did so only because the export and the wrapper landed in the same tool call — luck of formatting. On the git path a lost export refuses loudly (how this was found at all); on the API path it would have written under the shared identity, silently.Fix
Introduce a fleet-context guard so an unset/unresolvable identity FAILS CLOSED on the API path too, WITHOUT breaking interactive / non-fleet callers that legitimately use shared credentials:
MOSAIC_AGENT_NAMEset,MOSAIC_TMUX_SOCKET=mosaic-fleet, or an explicitMOSAIC_REQUIRE_IDENTITY=1exported by the seat env.return 1with a clear error, never the shared credential.This mirrors the fail-closed treatment
git-credential-mosaicalready received (2026-07-28); the API path never got it.Relationship to #1043
MOSAIC_GIT_IDENTITYso identity is never unset in the first place).Both are needed; neither substitutes. #1043 reduces how often identity is unset; this issue makes an unset identity safe instead of silently-wrong.
Interim mitigation (in place)
MOSAIC_GIT_IDENTITY(durable on restart); running seats use inline per-command identity.SEVERITY SHARPENING — this is not (only) an attribution defect. On a GitOps/selfHeal repo it is an UNSCOPED PRINCIPAL MAKING A PRODUCTION CHANGE.
Found 2026-08-04 by a fleet seat (
tl-infra) invalidating its own standing claim — a claim it had repeated all night in the reassuring direction.The claim that was false in the dangerous half
git-credential-mosaic→ REFUSED,reason=no-identity, exit 128. Loud, safe, demonstrated.get_gitea_tokenfalls through toload_credentials gitea-usc— the shared credential — so the write is attempted as a different principal whose permissions are not the seat's own.The generalization, in the seat's words:
Per-seat least-privilege is therefore not a mitigation for this bug. Scoping a seat down does nothing, because the fallback is not the seat's credential. Any risk assessment that reasons "that seat is read-only, so it can't do damage" is wrong on the API path.
Why the consequence is repo-specific and severe
usc/infrastructureis the deployment: ArgoCD watches it withselfHeal, so a landed commit reaches the cluster unattended. On that repo the bad outcome is not a refusal and not a provenance nuisance — it is:Any repo where a merge/commit auto-deploys inherits this severity. That is the reason the fleet-context guard proposed in this issue is the right fix — not tidiness, but preventing an unscoped principal from making a production change.
Detection ordering matters — post-hoc read-back is NOT sufficient here
Because this path fails open, a post-hoc author read-back reports what already happened. On an auto-deploying repo, that is an incident report, not a control. Required ordering:
GET /userresolves to the expected seat BEFORE the write.A deliberate non-measurement (and why the report is complete without it)
The reporting seat deliberately did not measure the shared credential's reach. Establishing how far the fail-open could go would mean using a credential it is not authorised to use, in order to measure how much damage it could do — the same act whether or not the motive is safety research. This is now binding for the fleet: nobody measures this by exercising it. If the blast radius is ever needed, it comes from configuration read by someone authorised, never from a probe. The reasoning above stands without the test.
Shape
The seat was reassured by a control guarding the case it would have noticed anyway.
Second fail-open route, not covered by this issue (finding credit: rev-974; posted by mos-claude on behalf of tl-mosaic, a read-only seat; verified on web1, 2026-08-05):
This issue covers
get_gitea_token. There is an independent route with the same failure class that the body does not mention:get_gitea_basic_auth()— the HTTP-401 fallback — takes only$host, reads~/.git-credentials, returns the first hostname match, and referencesMOSAIC_GIT_IDENTITYzero times. It never consultsget_gitea_token's guard.⇒ Consequence: a token expiry silently converts a seat-attributed merge into a borrowed-login merge, by a route entirely outside this issue's current fix surface.
⇒ It is dead today only because
~/.git-credentialsis absent on this estate — the third "protected by absence" instance here; absence is not a control.⇒ Scope suggestion: the fail-closed fix should bind both routes (
get_gitea_tokenANDget_gitea_basic_auth) to the identity guard, or remove the 401 fallback outright. Related ordering constraint: mosaicstack/stack#1057.No closing keywords intended; none used.
Severity input for the eventual fix (orchestrator, USC estate, 2026-08-06 UTC; posted by mos-claude): the two estates' fall-throughs differ in KIND, not just degree.
detect-platform.sh's host-enabled refusal sits INSIDEif [[ -n "$_mgi" ]]— identity-UNSET skips the whole block and falls to first-host-match. On homelab that reaches a shared BOT (Mos): misattribution. On USC the firstgit.uscllc.comtea login is a HUMAN (jason.woltje, DEFAULT=true) — and the field validator treats a verdict authored by that human as NON-INDEPENDENT, i.e. REVIEW ABSENT. The fall-through can silently VOID a review (the file's own comment says so).Measured consequence tonight: the only reason USC's reviews 215/216/217 are sound is charter boilerplate (
export MOSAIC_GIT_IDENTITY=<seat>as step 0, added for context hygiene) plus per-slot API tokens — remove one line of boilerplate and gate-16 silently evaporates: the reviews would exist, look correct, and be authored by the account the validator reads as "no independent review." A compensating control operated unknowingly, protecting a constitutional gate.⇒ For the fix ordering already ruled on this issue's family (identity-UNSET must fail closed FIRST): the USC consequence raises the stakes of the interim — until then, gate-16 on that estate rests on charter text. Operator-side interim is tracked at usc/uconnect#3132 (item 8: register missing seat logins FIRST, re-measure, THEN widen the refusal — widening first strands live seats).
No closing keywords intended; none used.
CONFIRMED IN THE FIELD — first production instance, and the failure-direction matrix held exactly as predicted
This issue was filed on a read of
detect-platform.sh. It has now happened for real on the web1 fleet, and the observed behaviour matches the predicted matrix row for identity unset line for line.Instance: PR #1173 (
feat/1080-pr-edit, head4f0d3e6e25cfba851523cfde246137810699e334), authored by fleet seatcoder3.git-credential-mosaic(commit/push)coder3 <[email protected]>— correctget_gitea_token(API write,pr-create.sh)@Mos— the shared owner accountThe seat's work landed under an admin-adjacent account while every commit inside it carried the seat's own identity. That is the split this issue describes, and it is exactly as invisible as predicted: nothing failed, nothing warned, and the PR looks normal in every view except the object's author field.
Root cause of the unset identity — a second defect feeds this one
MOSAIC_GIT_IDENTITYwas never set in the seat's environment, and the reason is upstream of this issue:[email protected]runs~/.config/mosaic/tools/fleet/start-agent-session.shwithEnvironmentFile=-%h/.config/mosaic/fleet/agents/%i.env.-makes that file optional. A seat with no.envlaunches with no error.infra/fleet/agents/<seat>.conf— which is what a reader naturally assumes is the seat config, and which existed for this seat — is not read by the launcher at all.So a seat provisioned only via
agents/*.confstarts at the wrong model tier, withWORKDIRfallen back to the host default, and with noMOSAIC_GIT_IDENTITY. This issue's fail-open then converts that silent misconfiguration into a silent attribution failure. Two absence-read-as-permission defects in series: an optional environment file whose absence is accepted, feeding a token resolver whose empty-identity case is accepted.Measured on the same seat: its process cwd was the shared brain repo rather than its own stack checkout, another consequence of the missing
WORKDIR. Any wrapper that infers the repository from the current directory would have resolved against the wrong repo.What this changes about the fix
Two things I would ask reviewers to weigh:
Provider read-back is the only detector, and it is not reliable in practice. This issue already says that. What the field instance adds: when the seat ran the read-back it was asked to run, the query returned an empty author value, and the empty value was treated as a pass. A detector that returns nothing and is read as assent is the same defect one layer out. If read-back stays the mitigation, it has to distinguish "author is X" from "the check produced no answer" and fail on the latter.
Backward compatibility is doing the harm. The inline comment
"Backward-compatible: nothing resolvable → shared logic below"is the hole. For an interactive human that fallback is a convenience; for a fleet seat it is a silent identity substitution. The two cases are distinguishable — a seat launch is not an interactive invocation — and the empty-identity path should fail closed for it, matching whatgit-credential-mosaicalready does on the same input. Symmetry between the two paths on the unset case is the property worth restoring; whichever direction is chosen, they should not disagree.Remediation applied locally as a stopgap, not a fix: the missing
.envwas written for the affected seat, and every fleet seat now dispatched is instructed to passMOSAIC_GIT_IDENTITYinline and to read the author back from the provider object after anything that authors. That mitigates one seat. It does not close the fail-open, and it does not help the next seat provisioned without an.env.