feat(auth): Authlib OAuth 2.1 core + S154 P0 repair — dark by default - #346
feat(auth): Authlib OAuth 2.1 core + S154 P0 repair — dark by default#346creator35lwb-web wants to merge 16 commits into
Conversation
…weep) Groq now serves qwen/qwen3.8-27b (Preview tier, 131k context, 16,384 max completion - model docs verified 2026-08-29). Additions: - PROVIDER_CONFIGS groq models += qwen/qwen3.8-27b (3.6 stays listed while Groq serves it); models_verified_at -> 2026-08-29 - GROQ_8K_TPM_MODELS += qwen/qwen3.8-27b CONSERVATIVELY (same family/size/ tier as live-proven 3.6; over-clamp only shortens completions, under-clamp repeats the v0.5.49 413 class); revisit on a live per-model probe - tests: catalog membership + discriminating TPM-set membership pin BYOK-catalog-only: no hosted routing, no CC-1 experiment surface (BYOK-on- lane runs are denominator-excluded; no window is running). Focused suites 317 passed / 0 failed. NO release identity yet - draft until authorized. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2WLyXrgH8dSXqKJiXW5Cu
…date T S151 D-151-7 bounded repair (forecast F-T-151-4): the frozen currency fixture as_of=2026-08-07 predated this PR''s groq models_verified_at 2026-08-29, tripping verification_date_in_future on two tests. The green-fixture day moves to 2026-08-29 - on the newest pin, inside every provider''s 90-day window (earliest: openai 2026-06-22, stale from 2026-09-21). The all-stale 2027-01-01 fixture and the real-clock health test are unchanged. No release identity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
Alton GO 2026-08-30 (AY/AZ registration-auth lane). Mechanism ships
complete but DARK: REGISTRATION_GATE_ENABLED defaults off, and runtime
behavior stays byte-identical to v0.5.62 except honest repairs.
- RegistrationGate (FastMCP middleware): when enabled, the four
execution tools (consult X/Z/CS + run_full_trinity) require a
server-verified registered UUID from the X-VerifiMind-UUID header;
hosted-key and BYOK alike. Discovery, template reads, pages stay
anonymous. Fail-closed on registration-store outage (retryable 503-
class denial), in-band structured denials, free-registration CTA only.
- registration_lookup.resolve_registration: reader-side union of
early_adopters + ea_registrations, status-aware (deletion_requested
revokes), positive-cache only. Closes the broken POST /register
bridge without write migration (no slot-cap or pioneer-tier side
effects).
- Telemetry: tool_invoked contract UNCHANGED (name-only dispatch
attempt); new tool_authorized {tool, registered_uuid} after
verification and tool_denied {tool, reason} with NO caller input;
trinity_run_started/completed gain registered_uuid only for verified
runs; denied calls emit zero lifecycle events.
- Honest repairs live now: POST /register db-None returns
persisted:false "NOT saved" (F-RES-1 parity); /whoami recognizes
lightweight registrations and reports lookup outages honestly; stale
Privacy v1.0/v2.5 page references corrected.
- Sybil bound: identity-minting POSTs (/register,
/early-adopters/register) leave the rate-limit exemption; page GETs
stay exempt.
- Terms v2.5 + Privacy v2.6 (EN+BM, one canonical source): announce the
requirement with gate-effective date 2026-09-30 (proposed; finalized
at release identity) - the 14-day advance notice, machine-checked by
test_gate_effective_date_honors_14_day_notice.
- get_public_contract().registration projects the flag truthfully in
both states.
Tests: 36 new (fail-closed inversion, denial walks, lifecycle join,
no-lifecycle-on-denial, bridge semantics, first HTTP-level POST
/register + /whoami coverage, 14-day notice); full unit suite 1213
passed locally (1 known env-only dependency-parity failure; CI is the
authority).
NO release identity: version stays 0.5.62; activation is a separate
Alton-held config flip on/after the policy effective date.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
… ladder Consumes T S152 BOUNDED SECURITY HOLD (P0 1-8) + T S153 OAuth addendum per the accepted Design v2 (private brief + PR #144). Everything stays DARK behind REGISTRATION_GATE_ENABLED (default off) - dark parity is test-pinned. - oauth/ package: narrow spec-exact OAuth 2.1 core, STDLIB CRYPTO ONLY (zero new dependencies - the design''s bounded fallback, expanded CS scope): authorization-code + PKCE S256-only, opaque store-validated tokens vm{at,rt,pat}.{id}.{secret} with SHA-256 verifiers at rest and constant-time compare; refresh rotation with reuse-detection lineage revocation; RFC 8414 AS metadata, RFC 9728 protected-resource metadata, RFC 7009 revoke, bounded RFC 7591 DCR (https/loopback/ private-scheme redirect URIs); enumeration-safe email ceremony (uniform "code sent", 8-digit code, 5-attempt cap, email stored as hash in verification docs); vendor-agnostic SMTP mailer that FAILS CLOSED in production (console backend refused on Cloud Run); PAT lane minted from a live access token (P0 #3, #1, D-153-4). - McpAuthBoundary (pure ASGI, inside the rate limiter): HTTP 401 + WWW-Authenticate with the PRM pointer / 503+Retry-After on store outage; connection mode default + execution mode (bounded body peek with replay, fails toward authentication); legacy X-VerifiMind-UUID header confers nothing (D-153-5). - RegistrationGate v2: subject only from the boundary contextvar; cross-subject user_uuid FAILS CLOSED (P0 #5); events renamed to what they prove - tool_admitted (admission only, P0 #8) and terminal tool_completed {success, inference_quality, environment, traffic_class, execution_id}; all events + trinity lifecycle carry an HMAC-pseudonymous subject, raw UUIDs never in value telemetry (P0 #6, unkeyed => field omitted, never raw). - Union revocation: opt-out now tombstones BOTH registration collections AND every live OAuth/PAT credential, success only when all stores answered (P0 #2); validation caches bounded to 60s with local purge (P0 #7); issuance limits keyed on server-observed properties only (P0 #4). - Policies: Terms v2.5 / Privacy v2.6 rewritten for the OAuth ceremony in EN + Bahasa Malaysia - UUID explicitly a NON-credential, tokens are the credentials, pseudonymized telemetry disclosed, transactional email processor disclosed (named at release), 14-day notice machinery unchanged and machine-checked. - Composition per Alton 2026-08-31: branch rebased onto #345 exact code-PASS head c8d1e8c (commits byte-identical ancestors) so ONE version release patch carries qwen currency + this spine. Tests: 1,333 passed / 3 skipped locally across unit + registration suites (116 in the five auth suites; sole local failure is the dependency-parity check refusing the stale dev environment - CI is the authority). CIMD fetch is a marked follow-up on the v0.5.56 pinned-dial fetcher; dark client matrix, CS review, T rebind, and every release/ activation authority remain ahead. NO release identity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
ea0395f to
6ab7e32
Compare
Design v2 implemented at
|
|
|
||
|
|
||
| def _db(): | ||
| from verifimind_mcp.registration import _get_firestore |
Alton selected Brevo (2026-09-01, cost-control lens: 300/day free SMTP relay, no card, EU processor) - decision point 1 of Design v2 closed. Privacy v2.6 section 7 and the Bahasa Malaysia mirror now name the processor instead of the identity-time placeholder; scope unchanged (recipient address + verification message only). Remaining identity- time fills: publication/gate dates only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
Consumes T S155 route + D-ALTON-2026-09-01-AUTHLIB-REGISTRATION-AUTH. Replaces the hand-written OAuth state machine with an exactly-bound Authlib 1.8.0 core behind a thin async Starlette adapter (no Flask/ Django, no client-integration misuse), and closes every S154 BOUNDED-SECURITY-HOLD finding plus the bypasses found by an internal attacker-position pass run before this SHA. WP-A dependency authority - Authlib==1.8.0 + joserfc==1.7.5 (issuer fix GHSA-r74j-q665-7rpj) + cryptography==46.0.1 pinned in pyproject.toml AND its requirements mirror; THIRD_PARTY_NOTICES.md (BSD-3-Clause + provenance); TestAuthlibDependencyAuthority added. The image-parity CI job reads exact pins from pyproject, so the real Python 3.12 image verifies these automatically. WP-B Authlib protocol core - AuthorizationServer + AuthorizationCode/RefreshToken grants, public clients only (auth method none), response_type code, scope mcp. - PKCE registered as REQUIRED and constrained to S256 (stock Authlib permits plain). Duplicate-parameter preserving payload so Authlib''s multi-param rejection actually fires. - Opaque hash-only tokens minted THROUGH the validated grant path. WP-C invariants Authlib cannot supply (S154 P0-1..P0-10) - P0-1 cache keyed on a digest of the COMPLETE credential + expiry cap; same-id/wrong-secret rejected cold AND warm. - P0-2 duplicate-email discloses no UUID/opt-out URL on either register path; dashboard and opt-out require the authenticated subject UNCONDITIONALLY (the flag-gated version left the default dark posture open to bare-UUID account tombstoning). - P0-3 minting bound INTO the transactional code claim; wrong PKCE does not consume; barrier test proves exactly one concurrent winner; refresh rotation + OTP cap transactional. - P0-4 issuer/audience/scope stamped and re-checked (absence DENIES); 401 wrong issuer/audience, 403 insufficient_scope with challenge. - P0-5 one bearer path accepts ACCESS+PAT, rejects REFRESH; revoking any credential tombstones the family AND descendants. - P0-6 STARTTLS with a validating ssl context; single-recipient enforcement (a To: list smuggled the OTP past the allowlist). - P0-7 independent default-off OAUTH_ISSUANCE_ENABLED blocks every mutating OAuth/DCR/PAT/ceremony path; metadata stays readable. - P0-8 env-bound issuer/resource/origin/collections; staging must declare its own origin AND any non-production K_SERVICE must declare its environment; account collections namespaced so staging cannot read/create/tombstone production accounts; PRM never falls back to production. - P0-9 exact redirect membership, no fragment/userinfo, agree-checkbox enforced, frame-ancestors/X-Frame-Options/no-store on ceremony pages, last-hop XFF (rate_limiter used the caller-controlled leftmost hop), global + per-action mint limits, unused-client expiry. - P0-10 verified-mailbox ceremony; peppered OTP bound to session+purpose; tool_completed gains route + protocol_era with an env-bound label. Adversarial-pass repairs (each with a discriminating regression) - PAT bound to its parent grant: a stolen access token could be upgraded to a silent 180-day credential surviving the victim''s revocation. - /oauth/pat now runs full bearer validation, not a bare store lookup. - Revocation tombstones consulted at validation time close both the cross-instance cache window and the query-then-update race. - Pre-registration subject hijack closed: legacy register paths mark records unverified and never hand out an identifier (also removing the account-existence oracle); the ceremony upgrades on mailbox proof. - Email canonicalized everywhere (case split forked accounts). - Firestore RPC failures re-raised as StoreUnavailable so an outage is a retryable 503, not 401 invalid_token. - Array JSON-RPC params no longer crash the boundary; oversized bodies replay intact. Tests: 1,343 passed / 3 skipped locally (21 OAuth-core, 18 boundary, 20 gate, bridge, parity). The single local failure is the documented dev-env pin refusal (installed fastapi/fastmcp/starlette/uvicorn differ; Authlib/joserfc/cryptography match) - CI installs the pins and is the authority. Full ceremony verified end-to-end through the real ASGI app. Deferred and NOT claimed complete: CIMD with SSRF-hardened retrieval and the SDK-backed MCP 2026-07-28 lane remain after CS pass 1 and the exact-client 2025-11-25 matrix; physical-deletion TTL receipts remain a retention follow-up. NO merge, staging, deployment, credential issuance, policy publication, notice, or activation. Both gates default OFF; production untouched at v0.5.62. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
| registration_gate_enabled, | ||
| ) | ||
|
|
||
| _PRM_URL = "https://verifimind.ysenseai.org/.well-known/oauth-protected-resource" |
| "code", grants authorization_code/refresh_token, PKCE S256-only. | ||
| """ | ||
|
|
||
| from typing import List, Optional |
| from authlib.oauth2.rfc6749 import ( | ||
| ClientMixin, | ||
| InvalidClientError, | ||
| InvalidGrantError, | ||
| OAuth2Request, | ||
| ) |
|
|
||
| import base64 | ||
| import hashlib | ||
| import re |
|
|
||
| warnings.filterwarnings("ignore", category=DeprecationWarning) | ||
|
|
||
| from verifimind_mcp.oauth import config, core, stores |
Independent CS review (Antigravity, exact head 2f44433) returned BOUNDED-HOLD with four findings; the crypto core, tombstoning, PKCE S256-only, and OCC token issuance were verified holding. Each finding repaired with a discriminating regression (tests/unit/test_cs_review_findings.py): - F1 dark-issuance completeness: the legacy POST /register and POST /early-adopters/register handlers lacked the issuance gate, so a public caller could still write Firestore records while dark. Added a _registration_dark_response() guard to both (scoped to account issuance; the feedback channel is not an issuance path). Dark -> 503, zero write; issuance-on -> proceeds. - F2 anonymous-registration orphaning: the oracle fix returned uuid="" for EVERY new registration, orphaning the maximum-privacy no-email user who then had no way to recover their identifier. register_user now branches on email: absent -> return the UUID (no email = no oracle/hijack surface); present -> withhold (uniform, oracle-safe). - F3 subject-hijack data injection: _resolve_or_create_subject adopted an unverified record's attacker-injectable display_name/name/feedback on mailbox proof. Those fields are now wiped when an unverified record is upgraded to verified; the UUID is adopted for continuity but no caller-set data survives the verification boundary. - F4 environment-isolation leak: registration_gate._denial_payload used a hardcoded production PRM/register URL, pointing a staging client at the production authorization server (lockout loop). Now resolved from config.current_environment(); a misconfigured env omits the PRM rather than leaking production. Tests: 1,351 passed / 3 skipped locally (8 new CS-finding regressions); sole local failure remains the documented dev-env pin refusal. Live smoke through the real ASGI app confirms dark blocks the write, the anonymous path returns its UUID, and the email path withholds. Back to independent CS for safe-to-stage re-review at the new exact head. No merge, staging, deployment, credential issuance, policy publication, notice, or activation. Both gates default OFF; production untouched at v0.5.62. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dmzqjtx5Fny8tQcc8fe5zB
…nse evidence (T S156) Bounded gate-truth repair against exact parent 7a0c462 (T S156 brief, D-156-1..7; activated by Alton 2026-09-02). One new SHA; no merge, staging, deployment, activation, routing, billing, pricing, policy, notice, or release-identity change. registration_gate._denial_payload: the two complementary `if` statements become one exhaustive if/else so `error`/`hint` are provably bound on every path - the github-advanced-security "CodeQL" results check at 7a0c462 FAILED with two py/uninitialized-local-variable errors (lines 206-207) plus eight notes. `_env_urls()` is hoisted above the decision (pure; already unconditional). Both denial payloads are byte-identical to 7a0c462 (1,656-case differential, 0 mismatches; `_env_urls` exactly once per call); any non-cross-subject reason still resolves to authentication-required. tests/unit/test_gate_truth_denial_payload.py (16): exact payload pins; every reason binds error/hint; environment binding for BOTH payloads; the REAL `_env_urls` fallback path never advertises a production PRM (class sweep: registration_gate.PRM_URL must stay dead); a definite-assignment structural pin (one `if`, terminal `else`, every arm definitely binds both - FAILS on 7a0c462, if/elif-no-else, nested-if, pre-init mask; PASSES on sound tuple/annotated refactors); CPython LOAD_FAST_CHECK oracle (3.12+). THIRD_PARTY_NOTICES.md: verbatim LICENSE bytes for Authlib 1.8.0, joserfc 1.7.5 and cryptography 46.0.1 (LICENSE / LICENSE.BSD / LICENSE.APACHE) with SHA-256 receipts bound to the PyPI artifact digests (wheel + sdist, verified), PEP 740 Trusted-Publisher attestations (authlib/authlib pypi.yml; authlib/joserfc pypi.yml; pyca/cryptography pypi-publish.yml), and the authlib v1.8.0 tag commit 1a86748b. tests/unit/test_third_party_notices.py re-verifies every block byte-for-byte against the installed pinned distribution (duplicate/stale blocks rejected). The eight notice-level CodeQL notes are inspected and left untouched; their bounded disposition is recorded in the PR body. No authentication, authorization, routing, provider, billing, pricing, release-identity, or activation semantics changed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
…und XFF trust (CS round 2) Bounded availability repair against exact parent d125bce, activated by Alton 2026-09-03 on the RNA S158 record. One new SHA; no merge, staging, deployment, activation, routing, billing, pricing, policy, notice, or release-identity change. No authentication or authorization semantics change. F1 (OOM): McpAuthBoundary._gated_call_peek buffers at most _MAX_PEEK_BODY. Once the cap is crossed it stops reading, marks the request protected, and the replay SPLICES - buffered messages first, then ALWAYS the live receive - so an authenticated caller's large body arrives intact (the S155 truncation concern) while an anonymous caller can never make the process buffer more than the cap; the 401 goes out with the remainder unread (safe under the pinned uvicorn 0.52.4: remainder discarded, keep-alive preserved). The same hand-over removes a pre-existing S155 defect found by the F1 adversarial lens: an endless empty http.request sentinel after the buffer made the SSE disconnect listener spin and froze the event loop on any anonymous non-gated initialize in execution mode. F3 (500 -> 503): stores.is_backend_failure walks __cause__ then __context__ (bounded) - the Firestore transactional wrapper raises a plain ValueError *from* the last Aborted after retries, and when BeginTransaction itself fails its rollback path raises ValueError('...cannot be rolled back') chained only via __context__ (the F3 lens refuted the first, cause-only repair on exactly that shape, end-to-end 500 through the real token handler). One _guarded(op) decorator maps backend failures (google.api_core, google.auth, grpc) to StoreUnavailable on _read, run_transaction, and every raw Firestore write/query; application signals and programming errors pass through. The authorize ceremony renders the honest 503 on any store outage (no more "invalid code" / "session expired" on outage), cleanup drops are best-effort, and _resolve_or_create_subject maps backend failures to the existing 503. F2 (XFF trust depth): new utils/client_ip.py resolves the client with an explicit TRUSTED_PROXY_HOPS (default 1 = the element the Google Front End appends last; 2 behind a Google external Application LB, proven by the ingress receipt), shared by rate_limiter.get_client_ip and oauth.endpoints._client_ip; a chain shorter than the trust depth falls back to the direct peer, never a caller-supplied element; the startup banner discloses the configured depth. Production receipt 2026-09-02: domain mapping via ghs.googlehosted.com, 0 forwarding rules, 0 serverless NEGs -> depth 1. Staging must re-bind to its own ingress. tests/unit/test_cs_round2_availability.py (49): real-wrapper Firestore shapes over a scripted GAPIC surface, chain-walk pins, a class sweep over ten raw store calls, ceremony outage -> 503, bounded peek + intact splice + SSE hand-over, and the trust-depth matrix; proven 33 failed / 16 passed against the d125bce modules and 49/49 on this head. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
…s not classified as a secret Second child SHA on top of d021f8c (parent d125bce). Pushed because the set-bound check-suite read at d021f8c found the github-advanced-security "CodeQL" results check red: py/clear-text-logging-sensitive-data (high) on the new startup-banner line. CodeQL's sensitive-name heuristic (maybeSecret: "...|(?<!un|un_|is|is_)trusted(?!_iter)|...") classifies any identifier, env-var name, or dict key containing "trusted" as a secret, so disclosing TRUSTED_PROXY_HOPS - an integer 1..8 - tripped it. Two py/empty-except notes on the best-effort session drops were introduced by the same commit. No behaviour change: TRUSTED_PROXY_HOPS -> INGRESS_PROXY_HOPS (env var, function names, dict key, documented in the module with the reason), and explanatory comments inside the two except bodies. Tests and the banner text follow the rename. The activation contract's "one new exact SHA" is therefore two SHAs; the deviation and its cause are disclosed in the PR body and the Hub record. Nothing was asserted green at d021f8c - the poller caught the red row before any evidence was written. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
…ronment isolation (CS round 3 / T S157) WP-A - refresh revocation completion (T S157 Finding 1; CS round 3 F1): - validate_refresh denies grant / parent-grant / subject tombstones after secret verification, the same coverage bearer validation already had. - rotate_refresh_tokens re-verifies the presented secret INSIDE the transaction, derives grant / parent grant / subject from the persisted old-token record, refuses any caller identity that disagrees (and an empty persisted grant or subject), reads the three tombstone documents THROUGH the transaction before marking the old token or writing descendants, checks kind/expiry, and writes descendants under the persisted identity WITH the parent link. A revocation that lands between those reads and the commit either conflicts the commit (retry re-reads, sees it, denies) or waits for it; in both orderings the descendants carry the tombstoned grant/parent id and every validation path refuses them. - Denials raise RefreshRejected, which save_token maps to Authlib's invalid_grant (400) - a denial, never a retryable 503. A replayed rotated token still revokes the whole family (RFC 9700 s4.14.2) and is then denied the same way (was: a 503 that invited retries against a dead family). - Found by the S160 adversarial lens, HIGH at fecc67c: validate_refresh and contain_refresh_reuse bound the token KIND to the caller-controlled wire prefix, so an ACCESS token rewritten vmat.->vmrt. resolved to its own record (same id, same secret, one shared collection) and rotated into a 30-day refresh family. Both now gate on the PERSISTED kind; the rotation transaction re-checks it. Unreachable in production today (endpoints dark). - Token format, hash-only storage, bearer/PAT behaviour, and rate limits are unchanged. WP-B - environment isolation completion (T S157 Findings 2/3): - registration.account_collection no longer swallows EnvironmentMisconfigured: a misdeclared staging identity stops BEFORE any client or I/O and never falls back to a production collection name. - Every user-data reader/writer resolves its collection through that one seam first: register_early_adopter, register_user, get_ea_status, process_optout, submit_feedback (both feedback write sites), registration_lookup, rate_limiter UUID tier resolution, trinity_history read + write. - http_server: EnvironmentMisconfigured -> explicit 503 service_misconfigured (Retry-After, no-store, no environment detail, "nothing was stored") on every HTTP surface; /register re-raises it instead of a generic 500. - Production and local development keep the bare historical names; only a declared staging is prefixed. Anonymous feedback stays admitted while issuance is dark (T D-157-5). The quarantined coordination store is untouched. Test fake: transactions now refuse a read after the first buffered write, as the real client does (ReadAfterWriteError), so a reorder that would 500 in production fails here instead of passing. Regressions - tests/unit/test_cs_round3_security.py (44): 33 discriminators FAIL on the fecc67c tree (sparse worktree, imports bound) for their stated reasons, including the deterministic two-party barrier race (tombstone commits in the rotation's pre-commit window -> conflict -> retry -> deny, nothing written) for grant, parent-grant, and subject tombstones, the concurrent double-refresh, the re-prefixed access/PAT tokens, and every misdeclared-staging surface (zero reads, zero writes, client never asked for); 11 pins pass on both heads (incl. production callers keep bare names, anonymous feedback admitted while dark, an outage stays an outage). Local gates on this tree: unit 1,382 passed / 3 skipped / 1 documented dev-env pin-parity failure; registration 84; integration 7 / 11 network-skipped; Bandit clean at CI flags. No merge, staging, deployment, authentication-gate activation, routing, billing, pricing, policy, notice, or release-identity change. Parent: fecc67c. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
…-only feedback attribution, uniform lane receipts, honest owner-route outages (CS round 4 / T S158) Contract 1/2 - canonical verified identity + consent provenance (T S158 F1): - One owner per email across BOTH registration lanes: a hash-keyed claim document (collection base "email_owners", environment-resolved) created with Firestore's atomic DocumentReference.create(); exactly one caller can ever create it. Both lanes scan both account collections for a legacy record first, then claim, then write the account record create-if-absent (a lane write can never clobber a record the ceremony healed); the loser receives the same uniform receipt. - The verified resolver (registration.resolve_verified_subject, called by oauth/endpoints) adopts through the claim, backfills legacy records into claims, heals a claim whose record write never landed (lane-default tier), creates a new verified subject claim-first, releases a claim that names a subject-tombstoned identifier and mints fresh, and fails closed on a malformed claim or an unusable legacy record. Collections resolve BEFORE the client (closes the round-4 ordering residual). - Mailbox proof is a state-transition sanitizer, not retroactive authorship: adoption neutralizes every caller-chosen field (profile, inline feedback, updates_consent -> False, tier -> lane default, pilot_source), stamps the verified actor's OWN Terms/Privacy acceptance, consent_source and registration moment (the earlier value kept as provenance), records what it neutralized, and detaches feedback linked before proof. Registration- path feedback is written with uuid=None and an explicitly unverified link. - process_optout releases the mailbox's claim after tombstoning the subject, so an opted-out address can register again and no email hash lingers. - Cohort (pioneer) rate tier requires a verified record; an explicitly unverified preregistration earns none (legacy records keep theirs). Contract 3 - feedback attribution (T S158 F2): - http_server._bearer_subject classifies absent / invalid / ok and lets StoreUnavailable, EnvironmentMisconfigured and programming errors propagate (no broad except); ea_feedback_handler returns 401 for a present-but-invalid Bearer and 503 for an outage, storing nothing; the authoritative uuid is the validated bearer subject or None, and a body uuid is kept only as claimed_uuid_unverified. Anonymous feedback stays admitted (T D-157-5). Contract 4 - enumeration resistance (T S158 F3): - One uniform receipt per email-bearing lane for new AND existing addresses (message, flags, tier, URLs, availability); the EA lane records feedback for both cases (unverified) so feedback_received is truthful and identical. Contract 5 - availability truth (T S158 F4): - Dashboard and opt-out answer a credential-store outage with a retryable, non-enumerating 503 (Retry-After, no-store; an honest outage page) and perform no history read or deletion; missing / invalid / wrong-subject credentials remain 401. Test fakes: FakeDocRef.create() raises AlreadyExists like the real client; FakeSnapshot.id. Legacy assertions follow the write primitive (set -> create); the hand-rolled opt-out fake gains delete(). Regressions - tests/unit/test_cs_round4_identity.py (39): 32 discriminators FAIL on the 2c43cd5 tree (sparse worktree, imports bound) at their stated first assertion — including two real interleavings (a competitor claims inside the lane's and the ceremony's window), a one-atomic-create-and- nothing-else pin, the opt-out/purge lifecycle, the heal-vs-set clobber, and the timestamp-echo oracle; 7 pins pass on both heads. Two adversarial lenses ran before this commit and their refutations are folded in. Local gates: unit 1,421 passed / 3 skipped / 1 documented dev-env pin-parity failure; targeted 418; integration 7 / 11 network-skipped; Bandit clean at CI flags; all 167 round-4 controls retained. No merge, staging, deployment, authentication-gate activation, provider routing, credential/spend, billing, policy, notice, or release-identity change. Parent: 2c43cd5. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
…e, resumable erasure, honest uniform storage outages (CS round 5 / T S159) One bounded repair at exact parent 68daa5c answering all four T S159 round-5 blockers. No merge, staging, deployment, activation, provider routing, credential/spend, billing, policy, notice, or release-identity change; both gates stay dark. F-01 (HIGH) Refresh containment. save_token's second validate_refresh returned None for a token rotated after grant-authentication and raised invalid_grant without containing the race winner. The save-time None branch now mirrors the first-validation branch: contain_refresh_reuse revokes the grant family before invalid_grant. Containment fires only for a secret-valid already-rotated token (wrong-secret/unknown revokes nothing); a backend outage on either read raises StoreUnavailable and propagates (503), never collapsing into a denial. F-02 (HIGH) ABA-safe ownership. The resolver released a tombstoned owner claim with a bare delete(), so two verified ceremonies could each erase the other's replacement and fork one mailbox into two verified subjects. _reclaim_tombstoned_owner reads the claim and installs the fresh subject in ONE transaction, bailing (re-read, adopt the winner) if the claim no longer names the tombstoned uuid it read; the fresh subject is minted inline through it, create-if-absent. _release_owner_claim deletes only while the claim still names the expected subject. F-03 (MEDIUM) Resumable erasure. Opt-out scrubbed PII and revoked the bearer before deleting the claim; a late delete failure left the caller revoked (401 on retry), the claim present, the key gone, and a later retry reporting success with the claim still there. New order, by reversibility: de-identify; release EVERY claim naming the subject by a reverse lookup on the claim's own uuid field (durable cleanup identity that survives the scrub and also cleans the inherited pre-repair state); tombstone + revoke LAST. Success is tombstone-authoritative: once the marker every validation path consults has landed, a failed hygiene sweep no longer denies an erasure that happened. Scrub-before-release closes the release-to-tombstone window by construction. F-04 (MEDIUM) Honest, uniform storage outages. Under reads-up/writes-down an existing address performed no write (201/200) while a new address hit claim_email().create() and escaped as 500 -- an existence oracle. Every email-bearing submission now performs one owners-collection write (new: create; existing: backfill a missing legacy claim, or re-assert an invariant field on the existing claim, which stores nothing new), so an outage fails both identically; both lanes then raise RegistrationStoreUnavailable and the handlers render ONE honest, retryable 503 (persisted:false, uuid "", Retry-After, no-store). A persistent quota or permission failure is loud, never a silent success (F-RES-1 preserved; the db-is-None construction branch keeps its ratified shape). Disclosed residual: a failure confined to the account collection alone remains asymmetric by construction and is not a realistic project-wide failure mode. Adversarial lenses before this commit (separate agent contexts, read-only): Lens A held F-01/F-02/F-03 across optimistic-fake and pessimistic-lock models, warm-cache and PAT descendants, and every opt-out failure point, and REFUTED the first F-04 draft (a uniform-success receipt on a write outage claimed persistence that did not occur and would have turned quota exhaustion into silent success) plus two LOW F-03 residuals (post-tombstone sweep failure reported as failure with a dead bearer; a residual claim after a ceremony in the release window with no reverse lookup) -- all redesigned here. Lens B audited the tests: two incidental old-head failures and six surviving mutants -- the suite was rewritten with whole-body receipts, real-bearer proxies, known-positives, a status-not-exception client, and its verified candidates; the final 18-mutant matrix has no survivor. Tests: tests/unit/test_cs_round5_identity_availability.py (24) -- 18 fail at 68daa5c at their stated first assertion, 6 pins pass on both heads; unit 1,445 passed / 3 skipped / 1 documented dev-env pin-parity failure; required targeted set + round-4 controls 206 passed; integration 7 passed / 11 skipped; Bandit: no new findings. Test fake: create() now passes a store-level write gate before its existence check (as the real commit does) and update() on a missing document raises NotFound; the legacy opt-out fake speaks the transaction protocol. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
| transaction refuses a read after its first buffered write. | ||
| """ | ||
|
|
||
| from typing import Any, Callable, Dict, Optional |
…edback write, tombstone-authoritative completion (CS round 6 / T S159) One bounded repair at exact parent 84fd926 answering all three T Round-6 blockers. No merge, staging, deployment, activation, provider routing, credential/spend, billing, policy, notice, or release-identity change; both gates stay dark. R6-01 (HIGH) Terminal erasure vs stale writers. The legacy read, the subject tombstone check, and the claim write were three separate operations in both the registration lanes and the verified resolver, so a caller that already held a live legacy subject could create a claim naming it AFTER opt-out's reverse-query sweep and tombstone returned success: a query is a snapshot and cannot exclude a later writer. Erasure now commits an ERASURE SEAL before sweeping the claims, and every existing-subject claim create / backfill / reassert goes through one transaction that reads the seal AND the subject tombstone through itself (_assert_claim_for_existing_subject). Both markers enter the transaction's read set, so a concurrent erasure either conflicts the commit -- the retry then sees the marker and refuses -- or waits for it, in which case the sweep that follows the seal removes what the writer wrote. Either way erasure wins. The seal is read by no credential validation path, so writing it before the sweep does not kill the caller's bearer and the operation stays resumable. A brand-new subject still uses the plain atomic claim_email(): an identifier no one has seen cannot already be erased. R6-02 (MEDIUM) Receipt truth vs partial persistence. The feedback add was the last write inside the fatal boundary, so a feedback-only failure returned the storage-outage receipt -- persisted:false, "no account was created" -- while the account holding the address AND that feedback text was already committed. The feedback document is auxiliary: its failure is now non-fatal, reported truthfully as feedback_received=false for a new AND an existing address alike (so it discloses nothing), and the account stands. Every remaining 503 therefore comes from the claim or account write, which makes the receipt's absence claim provable -- and the regressions assert the absent account, not merely response equality. R6-03 (MEDIUM) Tombstone-aware completion. Success after a failed hygiene sweep depended on an IMMEDIATE confirmation read; when that read failed too, the caller was told the erasure had not happened and to retry with a bearer the committed tombstone had already killed. stores now separates write_subject_tombstone() -- whose normal return IS the proof the marker landed -- from sweep_subject_credentials(), pure hygiene whose failure cannot un-do an erasure the tombstone already made effective. A failure of the tombstone write itself still raises: that commit is genuinely ambiguous, and the receipt says only that deletion could not be confirmed while naming the private rights channel, a continuation that does not need the bearer. R6-05 (LOW, harness) The fake invoked its write gate twice per logical write; create()/update() now pass the gate exactly once and mutate through an ungated _apply_write, so a one-shot fault injection fires once, as one commit RPC would. Deferred, recorded not fixed (T's instruction): R6-04, _count_tier_slots() converting an aggregate-count exception to zero, so a count-only failure can exceed the cohort cap. It is an availability/data-integrity issue with no staging credential or privacy effect and belongs to its own bounded lane. Tests: tests/unit/test_cs_round5_identity_availability.py 24 -> 30. At the exact parent 84fd926: 6 fail / 24 pass. Four fail behaviourally at their intended first assertion -- a claim naming the erased subject survives a completed opt-out (registration lane and verified resolver), 503 instead of 201 for the feedback-only case, and processed=false after a committed tombstone -- and two fail because the seal primitives do not exist there. Retained: all 230 targeted controls (236 with the new tests). Unit 1,451 passed / 3 skipped / 1 documented dev-env pin-parity failure; integration 7 passed / 11 skipped; Bandit no new findings. Also cleared the four CodeQL test-style notes S162 disclosed and deferred. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
| ) | ||
| feedback_stored = True | ||
| except Exception as exc: # noqa: BLE001 | ||
| from .oauth.stores import is_backend_failure |
| type(exc).__name__, | ||
| ) | ||
| except Exception as exc: # noqa: BLE001 | ||
| from .oauth.stores import is_backend_failure |
| # lane's honest not-saved contract rendered as a retryable 503. | ||
| logger.warning( | ||
| "Lightweight registration storage unavailable (error_type=%s)", type(exc).__name__, | ||
| ) |
| # not-saved behaviour (re-raised to the handler). | ||
| if is_backend_failure(exc) and data.email is not None: | ||
| logger.warning( | ||
| "Lightweight record storage unavailable (error_type=%s)", type(exc).__name__, |
| return ops | ||
|
|
||
|
|
||
| _WRITE_OPS = {"create", "set", "update", "delete"} |
…ils closed (S163 lens, HIGH self-regression) Corrective commit to the CS round-6 repair. My own adversarial lens, run against cedc24b before handing the head to round 7, refuted part of it. Disclosed plainly: this bounded repair therefore took TWO commits rather than the one the GO asked for. The alternative was to hand the independent seat a head carrying a HIGH regression I already knew about, which is worse. HIGH -- an interrupted erasure was forked into a fresh subject, stranding the original. cedc24b introduced the erasure seal by WIDENING one shared predicate (_subject_revoked -> seal OR tombstone). That predicate also gated the resolver's reclaim decision, which asks a different question: not "may I bind new state to this subject?" but "is this mailbox free again?". An erasure interrupted after the seal -- its claim release failed, so the caller keeps a live bearer and is expected to retry -- then looked revoked to that branch. The next sign-in minted a FRESH subject with a new active account carrying the address in clear, and stranded the original: never tombstoned, credentials alive, unreachable from the owner route. At the round-6 parent the same state failed closed. That is a regression, and it defeats the resumability the seal exists to preserve. Split the predicate. _subject_revoked keeps seal-or-tombstone and answers "may I bind new state to this subject?" -- used by the transactional claim guard and the legacy-backfill refusal. New _subject_erasure_complete is tombstone-only and answers "is this mailbox free again?" -- the only predicate that may trigger a reclaim. A seal without a tombstone now fails closed with an explicit log, exactly as the parent did, and the caller finishes the erasure with the bearer that state deliberately left alive. LOW -- write_subject_tombstone("") returned normally while _write_tombstone silently dropped the empty key, so "a normal return is the proof it landed" was false for that input -- precisely the contract R6-03 rests on. It now raises. Unreachable through the HTTP route today. LOW -- subject_is_sealed_or_revoked had no empty-uuid guard where its non-transactional twin does, so a careless call site could read two absent markers and conclude "not erased" for a subject that does not exist. It now refuses, mirroring subject_is_erased. LOW -- the hygiene-sweep handler caught bare Exception, swallowing programming errors behind a warning about a backend condition. Narrowed to StoreUnavailable; sweep_subject_credentials is guarded, so a real bug propagates again. Not changed, and re-disclosed rather than certified: the account write exists only on the new-address branch, so an account-collection-only outage still separates new from existing (201 vs 503). The lens confirms this is identical at the round-6 parent -- pre-existing residue, already on T's own deferred list -- but cedc24b's framing ("every remaining 503 comes from the claim or account write") asserted it away. It is a residual, not a closed item. Tests: 30 -> 33. The new discriminator fails at cedc24b with "an unfinished erasure handed the mailbox to a fresh subject"; two pins cover the finished- erasure side (still reclaims, as S161/S162 established) and the empty-subject refusals. The F-02 interleaving gate now wraps BOTH erasure predicates with one shared counter, so the seam survives however the branch is spelled at 68daa5c, 84fd926 and here. Differential vs 84fd926: 8 failed / 25 passed. Targeted set 239 passed (all 230 round-6 controls retained); unit 1,454 passed / 3 skipped / 1 documented dev-env pin-parity failure; integration 7 passed / 11 skipped; Bandit no new findings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
| Non-transactional: a caller that also WRITES a claim naming this subject | ||
| must use ``_assert_claim_for_existing_subject`` instead, which re-reads | ||
| both markers inside the transaction that performs the write.""" | ||
| from .oauth.stores import subject_is_erased |
…lose four surviving mutants (S163 lens B) Tests and comments only. No source change; the repaired behaviour at 07973b4 is unchanged, and every one of its 239 targeted controls still passes. My second adversarial lens audited the regression file and found that the evidence I was about to hand the independent seat contained statements that are not true. Correcting them is not optional, so this is a third commit -- disclosed, like the second. FALSE CLAIMS CORRECTED * The module docstring said "tests marked pin: pass at both heads". Three tests labelled pin: FAIL at 84fd926 with AttributeError, because they touch store primitives this repair introduces. The docstring was therefore false about its own contract. Those three are now labelled by the head they discriminate against, and the docstring states plainly that a symbol-absence failure proves an addition, not a behaviour. * I claimed the 84fd926 differential was "six behavioural / two symbol". Measured, it is FIVE behavioural and THREE symbol-absent -- and one of the five (the interrupted-erasure test) fails there on its seal-state precondition rather than on its own stated outcome, so it is relabelled to cedc24b, the head where it fails at the intended assertion. Every artifact that repeated the wrong split is corrected with it. * test_anonymous_write_outage_is_not_a_success_receipt asserted only `status != 200` -- satisfied by an uncaught 500, which is exactly what the anonymous lane returns. A negative assertion was concealing the behaviour inside a class named "a storage outage is neither an existence oracle nor a false success". Renamed to what it proves, and it now asserts the 500 explicitly as the disclosed availability-truth residual it is, plus the property that actually matters: no phantom identifier. * _same_receipt's docstring claimed "every field" while skipping content-length whenever a field is dropped. Docstring now says which comparison runs and why. FOUR SURVIVING MUTANTS CLOSED (each verified to fail with the mutation applied and pass without it; tree restored byte-identical after each) * M4/M5 -- the transactional guard must read BOTH markers, and nothing distinguished them: the interleaving tests run a complete opt-out inside the writer's window, so the tombstone alone conflicts it and dropping either read still passed. Both one-marker states are reachable in production: an erasure interrupted after the seal leaves a seal with no tombstone, and revoke_all_for_subject -- plus every marker written before this repair -- leaves a tombstone with no seal. * M12 -- the committed-tombstone test failed only reads keyed subject_, while subject_is_erased reads erasure_ first, so a reinstated confirmation read would have been satisfied by the seal and the test would still have passed. Failing every marker read pins the contract: after the tombstone write returns, nothing is read. * M15 -- every owners write the count assertion sees goes through a transaction, single-gated before R6-05 too, so the fake's own fidelity change was unverified by its own suite. Tests 33 -> 36. Differential vs 84fd926: 10 failed / 26 passed (was 8/25); seven behavioural, three symbol-absent. Targeted set 242 passed, all 230 round-6 controls retained. Unit 1,457 passed / 3 skipped / 1 documented dev-env pin-parity failure. Lens B's remaining open items are recorded, not fixed: the slot-cap check raises and returns 0 in every test because the fake has no count(), so the cap is unenforced throughout the suite (this is the harness face of the deferred R6-04); and the account-only, seal-to-tombstone-resumability, and resolver-reassert prefixes need a contract decision before a test can pin them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU
|



What this is
The registration-auth lane, now an Authlib-based OAuth 2.1 authorization server per
D-ALTON-2026-09-01-AUTHLIB-REGISTRATION-AUTH, replacing the hand-written state machine. Everything ships dark behind two independent default-off gates.Exact head:
59424c57406cbe536cf03565edef310024f7167f(was84fd926; advanced by the CS round-6 erasure-seal / receipt-truth repair below — three commits:cedc24b52dd282615525f9c4f7460b31db500568on84fd9265701a38766bb6d3d8aa3cc3a0de77745b, then59424c57406cbe536cf03565edef310024f7167fafter RNA's own adversarial lens refuted part of it, then this evidence-integrity child after a second lens found false claims in the regression file's own comments; the deviation from the one-SHA instruction is disclosed in the round-6 section.84fd9265701a38766bb6d3d8aa3cc3a0de77745bis one child of68daa5c189d758bd1da2edb63cf42b25140ae2d4, itself one child of2c43cd532790a6bf5d5dee184cd3898db2ff8dea, itself one child offecc67cfe5712d123895db1decac02cc91eedecd; the two S159 child commits ofd125bce47c521dee91ebc2ab5fb2038a243e702aare explained under Two SHAs, with cause in the round-2 section, andd125bceis the T S156 gate-truth repair on7a0c462). No merge / staging / deployment / credential issuance / policy publication / notice / activation is requested by this SHA. Production remains v0.5.62, serving revision00504-c8d(image digest unchanged from00503-rxh; config-only hygiene 2026-09-01). Composition: this branch contains PR #345 (c8d1e8ca) as byte-identical ancestors per Alton's one-release decision.Architecture (Authlib as protocol core, VerifiMind owns state)
oauth/authlib_server.pyruns Authlib'sAuthorizationServer+AuthorizationCodeGrant+RefreshTokenGrantbehind a thin async Starlette adapter (VMOAuth2Request/VMPayloadpreserve duplicate params). Authlib owns standards parsing, grant/error validation, redirect/client binding, PKCE, bearer structure, DCR parsing, metadata. PKCE is constrained to S256 (S256OnlyCodeChallenge— stock Authlib permitsplain). Public clients only (token_endpoint_auth_method=none), grantsauthorization_code+refresh_token, scopemcp. Tokens stay opaque, hash-only at rest (vmat/vmrt/vmpat.{id}.{secret}), minted through the validated grant path.P0 findings → fixes (each with a discriminating regression)
stores.validate_bearercache keyed oncredential_digest(full credential), not token_id; secret verified on miss; expiry re-checked; revoke clears cachetest_oauth_core.py::TestCacheSecretBypass(wrong secret rejected before AND after warm-up)_authenticated_subject == uuidUNCONDITIONALLY (flag-gating left the default dark posture open)test_v0513/test_registrationdup-email non-disclosure; boundary owner-scope testssave_token→claim_code_and_mint_tokens); refresh rotation transactional; OTP attempt-cap transactional; optimistic-concurrency + retryTestConcurrency(exactly one winner via barrier), wrong-PKCE-doesn't-consumeVMBearerValidator/authenticate_bearerre-check vs env; 401 wrong issuer/audience, 403 insufficient scopeTestBearerAuthorization, boundary 403 testgrant_idfamily; refresh-reuse revokes familyTestBearerKinds,TestRefreshreplay-revokes-familystarttls(context=ssl.create_default_context()); fail-closed on send errorOAUTH_ISSUANCE_ENABLED(default off) blocks EVERY mutating OAuth/DCR/PAT/ceremony path; metadata stays readableoauth/config.py); stage/prod tokens mutually rejected by audience; mail recipient allowlistTestStageProdIsolationWP-A dependency authority + third-party-license evidence
Authlib==1.8.0(BSD-3-Clause) +joserfc==1.7.5(≥1.7.3 issuer-validation fix, GHSA-r74j-q665-7rpj) +cryptography==46.0.1pinned in bothpyproject.tomlandrequirements.txt; parity tests extended (TestAuthlibDependencyAuthority).THIRD_PARTY_NOTICES.mdnow reproduces each pinned artifact's license file verbatim (five files: AuthlibLICENSE; joserfcLICENSE; cryptographyLICENSE,LICENSE.BSD,LICENSE.APACHE) with SHA-256 receipts binding each block to the exact PyPI release artifact (wheel + sdist digests verified against PyPI's published digests; license bytes verified identical across every artifact and the installeddist-info/licenses/copy; Authlib tagv1.8.0→1a86748b). Publisher provenance verified via PyPI's integrity API: every listed Authlib/joserfc/cryptography artifact carries a Trusted Publishing attestation bundle from its GitHub publisher (authlib/authlibpypi.yml,authlib/joserfcpypi.yml,pyca/cryptographypypi-publish.yml) — the earlier notice's unverified "(Trusted Publishing provenance)" parenthetical is replaced by this checked statement.tests/unit/test_third_party_notices.pyre-verifies every block byte-for-byte against the installed pinned distribution in CI. No license text paraphrased; no legal-certification claim.Internal adversarial pass (run before
2f44433)Four independent attacker-position reviews were run against the implementation; the token core held (digest-keyed cache, transactional claim, PKCE matrix, OTP cap, refresh rotation + family revocation, STARTTLS), and the following real bypasses were found and repaired in that SHA, each with a discriminating regression:
parent_grant_id;revoke_grant_familysweeps descendants/oauth/patcalled the store directly, skipping issuer/audience/scopeauthenticate_bearer; ACCESS-only (no PAT chaining)/registerplanted the email→UUID record the verified ceremony would adopt (pre-registration subject hijack) + account-existence oracleTo:list smuggled the OTP past the recipient allowlistK_SERVICEmust declare its environment; PRM never falls back to productionparamscrashed the boundary (500)X-Frame-Options/frame-ancestors/no-store; params type-guardIndependent CS review → BOUNDED-HOLD → repaired (
7a0c462)The independent CS safe-to-stage review (Antigravity) verified the crypto core, tombstoning, PKCE S256-only, and OCC token issuance HOLD, and returned BOUNDED-HOLD with four findings — all repaired at
7a0c462, each with a discriminating regression (tests/unit/test_cs_review_findings.py):/register+/early-adopters/registerbypass the dark-issuance gate (public Firestore write while dark)uuid=""for every registration, orphaning the no-email anonymous userdisplay_name/feedbackon mailbox proofregistration_gate._denial_payload(staging → production auth server lockout)config.current_environment()Prospective correction of the earlier evidence line. This body previously stated "CI 10/10 green at
7a0c462". That statement was never bound to the completed check-suite: theCodeQLcheck from thegithub-advanced-securityapp — the code-scanning results check GitHub creates when theCodeQL Analysisworkflow job uploads its SARIF, evaluating new alerts in code changed by this pull request — completed at 2026-09-01T15:15:26Z with FAILURE: "10 new alerts including 2 errors" — two error-levelpy/uninitialized-local-variableannotations (error,hintatregistration_gate.py:206-207) plus eight notes. Final check-suite at7a0c462: 10 check-runs, 9 success / 1 failure,mergeable_state=unstable. (Two runs share the nameBandit SAST Analysis: the real scan fromsecurity-scan.yml, and the synthetic instant-pass job of the same name fromdocs-ci-bypass.yml, which triggers because this PR also touches**.mdfiles — it runs onlyverify_public_docs_contract.py. The ruleset's sole required context is that name; that a synthetic job can satisfy it on a PR that also changesmcp-server/**is a pre-existing gate-design observation, recorded here and not changed.) T S156 caught this on a fresh exact-head read. (The repository ruleset formally requires onlyBandit SAST Analysis; T's review contract requires every current check green, and the red results check is what CS re-review, T rebind, staging, and merge are held on.)7a0c462was therefore not a green review target; the merged private records that repeated the 10/10 claim are preserved as history and corrected prospectively in the Hub.T S156 gate-truth repair (head
d125bce47c521dee91ebc2ab5fb2038a243e702a)The finding.
_denial_payloadbuilt the two denial payloads with two independentifstatements (if reason == DENIAL_CROSS_SUBJECT: …thenif reason != DENIAL_CROSS_SUBJECT: …). Logical complements to a reader — but not provably exhaustive to static analysis, so CodeQL flaggederror/hintas potentially uninitialized at the point of use. T bounded it as control-flow/static-analysis ambiguity, not a demonstrated bypass; the required gate was nevertheless red.The repair (one commit, +10/−2 in the module). The decision is now one exhaustive
if/else;register_url, prm_url = _env_urls()is hoisted above it (pure resolver, already called exactly once on every path, cannot raise). Neither payload changed — the strings, key order,error_codemapping, env-boundregister_url/resource_metadata, and the prior fall-through semantics (any non-cross-subject reason → authentication-required) are byte-identical. No authentication, authorization, routing, provider, billing, pricing, release-identity, or activation semantics changed.Discriminating tests (
tests/unit/test_gate_truth_denial_payload.py, 16 tests): exact-string pins for both payloads (incl. the no-PRM fallback clause); every admitted reason — and an unknown reason — always bindserrorandrecovery_hint; environment binding for both payloads (CS F4 extended to the cross-subject payload: staging never points at production); the real_env_urlsexception path (staging with no resolvable origin → no PRM anywhere in the payload — see the class sweep below); a structural AST pin that exactly ONEifbindserror/hint, the if/elif chain ends in a real terminalelse, and every arm definitely binds both names (definite-assignment semantics — a binding under a nested non-exhaustiveifdoes not count, so the pin covers the nested shape on the 3.11Run Testsjob); and an independent CPython oracle —error/hintcompile toLOAD_FAST(proven bound), notLOAD_FAST_CHECK(3.12+;7a0c462compiled them withLOAD_FAST_CHECK). The structural test fails on the7a0c462source (binding_ifs=2, has_else=[False, False]) and passes on this head — proven by running the same AST logic against both sources.Independent adversarial pass before push (six read-only lenses, each trying to refute one claim): payload byte-equivalence held over a 1,656-case differential (9 reasons × 8 tool names × 23
_env_urlsoutcomes incl. realoauth.configpaths; 0 mismatches;_env_urlscalled exactly once per call); CodeQL satisfiability derived from the query's own SSA/taint source (UninitializedLocal.ql/Undefined.qll) plus the CPython oracle, and the fullsecurity-and-qualitysuite reasoned against the rewritten lines with no new error-level query firing; license fidelity, eight-note disposition, test discrimination and contract scope each held. Six minors surfaced across the lenses and all were taken — every one a test or prose precision fix, none cleanup: the structural pin accepted anif/elifchain with no terminalelseand, separately, a binding under a nested non-exhaustiveif(now definite-assignment semantics); no test exercised the real_env_urlsfallback path; the notice test collapsed duplicate blocks to the last copy; the notice's scope sentence overclaimed ("every pinned component" → the three OAuth pins) and one paragraph tracked license clauses in prose (replaced by a plain endorsement disclaimer). Follow-up recorded, not made (workflow change): theLOAD_FAST_CHECKoracle only executes on 3.12+, while the only pytest job runs 3.11 — running the gate-truth file inside the built 3.12 image in the parity job would put the oracle on a required gate.The eight notice-level notes — inspected, dispositioned, left untouched (T: repair only a note that blocks the gate or exposes a real defect; no general cleanup):
mcp_auth_boundary.py:34unused global_PRM_URL_prm_url()returns the env-bound PRM orNoneand never consults it. The re-wire hazard is already guarded bytest_auth_boundary.py::test_misconfigured_env_never_challenges_toward_production. Not a defect; left untouched (cleanup candidate).authlib_server.py:12List,:17InvalidClientErrorunused imports__all__, re-exports, or getattr-by-name; sibling names on the same import statement are used, so no import-time side effect changes. Left untouched.stores.py:69↔registration.py:562cyclic import_db();process_optoutunderif matched:); the module-level import graph is acyclic and neither module calls the deferred edge at import time. Import-order probe in fresh subprocesses (stores-first, registration-first,verifimind_mcp.server,http_server,oauth.authlib_server,oauth.endpoints,middleware.mcp_auth_boundary) all import cleanly; both alerts predate the Authlib head. Left untouched.oauth_fakes.py:13Any,test_oauth_core.py:13re,:22configunused imports (test files)retoken is inside a comment;configshares its import statement with used names). Left untouched.Gate-neutrality receipt: at head
2f44433the same app check concluded success with "8 new alerts — 8 notes" (and 5 notes at6ab7e32/5f9b83d); it turned to failure only at7a0c462when the two error-level alerts appeared. Notes do not fail the results check, and the ruleset counts none of them.Class sweep beyond the eight (found by the adversarial pass, not by CodeQL):
registration_gate.py:49PRM_URLis the same dead-production-constant class as_PRM_URL, but no test exercised the real_env_urls()exception path — a future re-wire (return REGISTER_URL, PRM_URL) would have passed every test. One discriminating regression added (test_misconfigured_staging_never_advertises_a_production_prm, both reasons): staging with no resolvable origin → the real path →resource_metadataabsent and no PRM path anywhere in the payload; the documented fallbackregister_urlis pinned explicitly.PRM_URLitself is left untouched (cleanup, not a defect); the_env_urlsdocstring's plural "module constants … fallback" wording is noted for a later bounded pass.Local gates at this head (imports bound to the exact tree —
verifimind_mcp.__file__printed from the clone; the global editable install points at a stale clone and must not be trusted): unit 1,289 passed / 3 skipped / 1 failed (the failure is the documented dev-envtest_exact_pins_are_the_versions_actually_installed: installed fastapi/fastmcp/starlette/uvicorn ≠ pins while Authlib/joserfc/cryptography match; CI installs the pins and is the authority), coverage 77.8%; registration 84 passed; integration 7 passed / 11 skipped — 1,380 passed across CI's three invocations, +22 over7a0c462(16 gate-truth + 6 notice tests). Bandit with CI's flags (-r src/ -ll -ii --skip B101): no issues. Safety and SonarCloud run remotely.Remote checks at
d125bce47c521dee91ebc2ab5fb2038a243e702a(read from the commit's check-suite after every check — including the asynchronous GitHub Advanced Security app check — had completed):Bandit SAST Analysis(posted twice)Security Scanworkflow, run 33643868406 — the real scan, advisory (|| true); andDocs Contract and CI Bypass, run 33643868407 — synthetic instant-pass, triggered by**.mdSafety Dependency CheckSecurity Scan— advisory (|| true)Security ScanTest Suite, run 33643868480Production Image Dependency ParityTest Suite— builds the Python 3.12 image and asserts the pins inside itRun TestsTest Suite— Python 3.11: unit + registration + integrationServer Health CheckTest SuiteSonarCloud Code AnalysisCodeQL AnalysisSecurity Scan— scan + SARIF upload (security-and-quality)CodeQL10 check-runs / 9 unique contexts, non-success 0; PR
mergeStateStatus: CLEAN(wasUNSTABLE). Read at 14:44:57Z after every expected name existed and every run had completed.CodeQL(github-advanced-security) annotations at this head: success — title "8 new alerts", summary "8 notes": exactly the eight dispositioned notes above (annotations 8/8 match), 0 error-level. Open code-scanning alerts for pr=346 by rule: 2×py/cyclic-import, 1×py/unused-global-variable, 5×py/unused-import, 0×py/uninitialized-local-variable(alerts #237/#238 closed). Code-scanning analysis onrefs/pull/346/merge: 2026-09-02T14:44:21Z, merge sha28983337b, results=8 (was 10 at7a0c462's merge7d75947a4).CS round 2 (2026-09-02) → bounded availability repair (head
fecc67cfe5712d123895db1decac02cc91eedecd)The independent CS safe-to-stage RE-review of
d125bce(Antigravity, static, bound to that head) returned NOT SAFE TO STAGE on three availability-class findings while verifying that the security core — tombstone/cache fail-closed, duplicate-parameter first/last split, email normalization, strict STARTTLS — HOLDS: the second independent round in which the core held. RNA verified each finding against the code and the live ingress (private record20260903_RNA_s158_cs_rereview_verified_findings.md); Alton activated the bounded repair; this head is the second of two child commits ofd125bce—d021f8c(the three repairs, 7 paths) andfecc67cfe5712d123895db1decac02cc91eedecd(a rename forced by a CodeQL false positive, 5 paths) — see Two SHAs, with cause below.tests/unit/test_cs_round2_availability.py(49 tests; the discriminators FAIL ond125bce: 33 failed / 16 passed against the old modules)_MAX_PEEK_BODYAUTH_BOUNDARY_MODE=execution(dark today). The F1 lens also found a pre-existing S155 defect in the same function: after the buffered messages the replay returned an emptyhttp.requestsentinel forever instead of awaiting the livereceive; the MCP streamable-HTTP path hands that receive to the SSE disconnect listener, which loops on it, so any anonymous non-gatedinitializein execution mode froze the event loop (whole-instance hang, reproduced on the real app; unreachable today — gate dark, defaultconnectionmode passes the live receive)receive(which also removes the sentinel hang). Oversized → protected; authenticated large bodies arrive intact (the S155 concern); anonymous oversized get the 401 with the remainder unread (safe under the pinneduvicorn==0.52.4: remainder discarded, keep-alive preserved); small bodies and mid-read disconnects decide and replay exactly as before — only the post-body tail changes, from a sentinel to the server's real streamd125bce: 64); authenticated → all 4 MiB delivered downstream with ≤17 reads buffered before the app is entered (d125bce: 64); small-body replay unchanged; disconnect mid-read still protected; SSE-listener regression: after the body the nextreceive()is the live one and returns the disconnect (d125bce: sentinel forever → spin)@firestore.transactionalretriesAborted×5 then raises a plainValueError("Failed to commit transaction in 5 attempts.") from last_exc; and when the very firstBeginTransactionRPC fails, the wrapper's rollback path raisesValueError("…cannot be rolled back")whose only link to the backend error is__context__. Neither is agoogle.api_coretype, so_backend_failurenever mapped them; non-retryable RPC failures escaped directly. The first S159 repair walked__cause__only and was refuted by the F3 adversarial lens on the begin-failure shape (end-to-end 500 through the real token handler) before any SHA was minted. The same escape class existed in every raw store write/query and in the ceremony's raw Firestore callsstores.is_backend_failurewalks__cause__→__context__(unless suppressed, bounded); one_guarded(op)decorator maps backend failures toStoreUnavailableon_read,run_transaction, and every raw Firestore write/query (10 sites); application signals (_RefreshReuseDetected) and programming errors pass through. Ceremony: any store outage → the honest 503 page (no more "invalid code" / "session expired" on outage; cleanup drops best-effort);_resolve_or_create_subjectmaps backend failures to the existing 503Transaction+transactionalover a scripted GAPIC surface): begin failures ×6 incl.RetryError/RefreshError, commit-Aborted-then-retry-begin-fails, exhausted retries, failing rollback, in-transaction read failure →StoreUnavailable(d125bceand the first repair: escaped); signal passes through as the same instance;KeyErrornot masked; chain-walk pins (cause, context, suppressed); class sweep over 10 raw store calls (d125bce: escaped); ceremony outage → 503 forverify_codeand session read (d125bce: 200 "invalid code" / 400 "expired"); subject resolution →None(d125bce: raised)X-Forwarded-Forcollapses per-IP limits behind a load balancerverifimind.ysenseai.orgis a Cloud Run domain mapping viaghs.googlehosted.com; 0 forwarding rules, 0 serverless NEGs, ingressall→ the Google Front End appends the client last. Behind a Google external Application LB the last element would be the LB. The assumption was unstated in two limiters — the standing_client_ipresidualutils/client_ip.py: one resolver with an explicitINGRESS_PROXY_HOPS(default1= GFE-appended last element;2behind the external LB — and the docstring now says why too-large is worse than a collapse: on an ingress that appends fewer elements than the depth,[-depth]is caller-supplied again), shared byrate_limiter.get_client_ipandoauth/endpoints._client_ip; a chain shorter than the trust depth falls back to the direct peer, never a caller-supplied element; the startup banner discloses the configured depth. Staging must re-bind the depth to its own ingress with the same receipt before the client matrix relies on per-IP mint limits. Lens differential at depth 1 (2,046 inputs × both resolvers): byte-identical for every input with ≥1 XFF element; the only differences are two degenerate no-XFF inputs (whitespace-onlyX-Real-IP; empty/Nonepeer) that now resolve to the direct peer/unknowninstead of''/None— strictly improving and unreachable on Cloud Run, where XFF is always presentd125bce); depth 2 → the client behind the LB (d125bce: the LB); short chain at depth 2 → direct peer (d125bce: the caller-supplied element); env parsing defaults and clamps; prior no-XFF fallbacks preserved per limiter; disclosure matches configurationNo authentication, authorization, routing, provider, billing, pricing, release-identity, policy, notice, or activation semantics changed. Three independent adversarial lenses (F1 ASGI/splice semantics; F3 exception taxonomy and end-to-end 503 mapping; F2 resolver equivalence + contract scope + regression discrimination) ran read-only against both trees before the commit: F1 HOLDS on every claim (27-scenario byte-identical differential; early-401-with-unread-body verified safe from the pinned
uvicorn==0.52.4source) and found the pre-existing sentinel hang, fixed by the same hand-over. F2/scope HOLDS (2,046-case differential × both resolvers × five depths; scope exact; discrimination confirmed), with two hardenings taken — a cap-derived stream bound so the F1 tests cannot go vacuous, and an honest "too large" docstring. F3 REFUTED the first repair:BeginTransactionfailures surface as the rollbackValueErrorchained only via__context__(500 end-to-end through the real token handler, on google-cloud-firestore 2.21.0 and 2.29.0), and the same escape class existed in every raw store write — repaired before any SHA was minted (chain walk,_guardedon eleven store entry points, ceremony 503, subject-resolution mapping) and re-verified with the lens's own instruments on the committed tree: 28-shape unit probe 0 mismatches; end-to-end probe → HTTP 503 for every begin-time failure (was 500), commit-time still 503.Two SHAs, with cause. The activation contract asked for one new exact SHA. The set-bound check-suite read at
d021f8cfound thegithub-advanced-securityCodeQLresults check red:py/clear-text-logging-sensitive-data(high) on the new startup-banner line — CodeQL's sensitive-name heuristic (maybeSecret:…|(?<!un|un_|is|is_)trusted(?!_iter)|…) classifies any identifier, env-var name, or dict key containing "trusted" as a secret, so disclosingTRUSTED_PROXY_HOPS(an integer 1–8) tripped it — plus twopy/empty-exceptnotes on the best-effort session drops. Nothing was asserted green atd021f8c; the poller caught the red row before any evidence was written. Repaired atfecc67cfe5712d123895db1decac02cc91eedecdwith no behaviour change:TRUSTED_PROXY_HOPS→INGRESS_PROXY_HOPS(env var, function names, dict key; the reason is documented in the module) and explanatory comments inside the twoexceptbodies. Both commits are in this PR; the head line and every receipt below bind tofecc67cfe5712d123895db1decac02cc91eedecd.Local gates at this head (imports bound to the clone,
verifimind_mcp.__file__printed): unit 1,338 passed / 3 skipped / 1 failed (the documented dev-envtest_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority; +49 overd125bce), coverage 78.7%; registration 84 passed; integration 7 passed / 11 skipped; Bandit with CI's flags (-r src/ -ll -ii --skip B101): no issues.Remote checks at
fecc67cfe5712d123895db1decac02cc91eedecd(read from the commit's check-suite after every expected name existed and every run completed, including thegithub-advanced-securityCodeQLresults check):Bandit SAST Analysis(posted twice)Security Scanworkflow, run 33725409151 — the real scan, advisory (|| true); andDocs Contract and CI Bypass, run 33725409118 — synthetic instant-pass, triggered by**.mdSafety Dependency CheckSecurity Scan— advisory (|| true)Security ScanTest Suite, run 33725409213Production Image Dependency ParityTest Suite— builds the Python 3.12 image and asserts the pins inside itRun TestsTest Suite— Python 3.11: unit + registration + integrationServer Health CheckTest SuiteSonarCloud Code AnalysisCodeQL AnalysisSecurity Scan— scan + SARIF upload (security-and-quality)CodeQL10 check-runs / 9 unique contexts, non-success 0; PR
mergeStateStatus: CLEAN. Read at 06:55:33Z on 2026-09-03 after every expected name existed and every run had completed. (The same read atd021f8cwas 9 success / 1 failure — see Two SHAs, with cause.)CodeQL(github-advanced-security) at this head: success — title "8 new alerts", summary "8 notes": exactly the eight pre-existing notes dispositioned in the T S156 section (annotations 8/8 match); 0 error-level. Thepy/clear-text-logging-sensitive-dataerror and the twopy/empty-exceptnotes reported atd021f8care gone. Open code-scanning alerts for pr=346 by rule: 2×py/cyclic-import, 1×py/unused-global-variable, 5×py/unused-import; 0×py/uninitialized-local-variable. Code-scanning analysis onrefs/pull/346/merge: 2026-09-03T06:55:13Z, merge shaa1156a5dd, results=8 (was 11 atd021f8c's merge5183aecbb).CS round 3 (2026-09-05) → bounded security repair (head
2c43cd532790a6bf5d5dee184cd3898db2ff8dea)The independent CS safe-to-stage round 3 of
fecc67c(Antigravity, static, bound to that head) returned BOUNDED-HOLD / NOT SAFE TO STAGE. T S157 accepted the exact-head hold while bounding its authority (the transcript proves checkout/rev-parsebut ran no repository test, runtime, deployment, or clean-worktree gate, omitted parts of the requested checklist, and cited one wrong source path), confirmed one reported blocker with exact-head probes, reclassified the second, and found a broader class the report did not surface (review.macp/reviews/20260905_T_pr346_cs_round3_bounded_hold.md; contract.macp/briefs/20260905_T_to_RNA_pr346_round3_bounded_security_repair.md). Alton activated the bounded repair; this head is one child commit offecc67cfe5712d123895db1decac02cc91eedecd(8 source paths + the test fake + 1 new test file). The S160 adversarial lens on WP-A found a fourth, HIGH-severity defect atfecc67cthat no review round had surfaced — see the last table row.tests/unit/test_cs_round3_security.py(44 tests; the discriminators FAIL onfecc67c: 33 failed / 11 passed against the old tree)grant_tombstone_present: True,late_refresh_accepted: True).validate_refreshchecked only the record's ownrevoked/rotated_to; the rotation transaction never read a tombstone;revoke_grant_familywrites the tombstone first and then sweeps by query, so a rotation that committed after the sweep's snapshot left a refresh descendant that validated on its own flags and could rotate forever (bearer descendants were already refused — bearer validation consults the tombstone). Classification per T: revocation correctness + bounded availability/EDoS, not a demonstrated protected-resource bypassvalidate_refreshdenies grant / parent-grant / subject tombstones after secret verification (same coverage as bearer).rotate_refresh_tokensre-verifies the secret INSIDE the transaction, derives grant/parent/subject from the persisted old-token document, refuses any caller identity that disagrees, reads the three tombstone documents THROUGH the transaction before marking the old token or writing descendants (the read set is what makes a concurrent revocation conflict the commit), checks kind/expiry, and writes descendants under the persisted identity. Denials raise the newRefreshRejected, whichsave_tokenmaps to Authlib'sinvalid_grant(400) — a denial, never a retryable 503 (before: a token found revoked inside the transaction surfaced asStoreUnavailable→ 503). Reuse detection, token format, hash-only storage, bearer/PAT behaviour, and rate limits are unchanged. Both interleavings are closed: tombstone before commit → conflict → retry → deny; rotation commits first → descendants inherit the tombstoned grant id and every validation path refuses themvalidate_refreshNone (fecc67c: record returned); real token endpoint → 400invalid_grant, zero descendant docs, old token unmarked (fecc67c: 200 + two live descendants); the descendant of a rotation that committed before the tombstone is dead on both bearer and refresh paths (fecc67c: rotated again); deterministic two-party barrier: the tombstone commits inside the rotation's pre-commit window → conflict → retry (new_transactioncalled exactly twice) → deny, nothing written, for grant and subject tombstones (fecc67c: 200 + descendants — the tombstone key was never in the read set); full family revocation in the window →invalid_grant(fecc67c:StoreUnavailable→ 503); concurrent double-refresh → loser deniedinvalid_grantand the whole family revoked incl. the winner's pair, per RFC 9700 §4.14.2 (fecc67c:StoreUnavailable→ 503, a denial reported as an outage); descendants keepparent_grant_id, so a parent tombstone landing AFTER the commit still kills them (fecc67c: descendants written without the link → alive); forged grant / forged subject / forged client / wrong secret / empty persisted grant-or-subject refused without writes, each bound to its stated reason (fecc67c: committed — the empty-identity case minted descendants no tombstone could reach); a store outage during rotation staysStoreUnavailable(pin); unrelated tombstones do not over-deny (pin); bearer + PAT +revoke_credentialcontracts pinnedfecc67c: token KIND bound to the caller-controlled wire prefixfecc67ctree: all token kinds share one collection and one{id}.{secret}scheme, andvalidate_refreshchecked only the presented prefix — so an ACCESS token rewrittenvmat.→vmrt.resolved to its own record (same id, same secret) and the refresh grant rotated it into a 30-day refresh family: 200, two new documents, the victim's access record markedrotated_to. A stolen one-hour token upgraded itself.contain_refresh_reusetrusted the wire kind the same way. Unreachable in production today (the OAuth endpoints are dark), present at every reviewed head of this PRvalidate_refreshandcontain_refresh_reusenow gate on the PERSISTEDkind(the same gate bearer validation already had); the rotation transaction independently re-checks itvalidate_refreshNone, endpoint 400invalid_grant, zero docs, victim's access unmarked and still valid (fecc67c: 200 + two docs + victim marked rotated); re-prefixed PAT → None / 400 / zero docs / PAT still valid (fecc67c: record returned; only Authlib's client check stopped the rotation); a revoked access token re-prefixed as refresh no longer drives reuse containment (fecc67c: it did)_registration_dark_responseis deliberately scoped to credential/account issuance; feedback is intentionally public (T D-157-5: an absolute-dark rule is an Alton product decision, not a mechanical repair). The real blocker: a staging service sharing the GCP project wrote the production-namedfeedbackcollection (T probe:dark_feedback_status: 201,collections_written: feedback).addand standalonesubmit_feedback) resolve their collection through the one environment seam BEFORE the client is opened; production keeps barefeedbackand its anonymous-feedback semantics byte-for-byte; staging writesstaging_feedbackstaging_feedbackonly, nothing infeedback, on both sites (fecc67c: productionfeedback); production with both gates dark → 201, collections touched exactly["feedback"], no account/credential/mail mutation, mailer untouched (pin); feedback is not an exempt path → the outer limiter applies (pin)misdeclared_staging_collection: early_adopters;persisted: True+collections_written: ea_registrationsfrom a misdeclared staging;production_record_seen_from_staging: True,staging_record_seen_from_staging: False).registration.account_collectioncaught every exception fromcurrent_environment()and returned the bare production name;registration_lookup,rate_limiter._resolve_uuid_tier, both feedback sites, andtrinity_history(both directions) used raw production namesexceptis gone:EnvironmentMisconfiguredPROPAGATES fromaccount_collection, and every user-data reader/writer —register_early_adopter,register_user,get_ea_status,process_optout,submit_feedback,registration_lookup.resolve_registration,rate_limiter._resolve_uuid_tier,trinity_historyread + write — resolves its collection through that seam BEFORE constructing a client or touching Firestore. Explicit fail-closed answers: a new app-levelEnvironmentMisconfigured → 503 service_misconfiguredhandler inhttp_server.py(Retry-After,no-store, names no environment detail, states that nothing was stored) for/register,/early-adopters/register,/early-adopters/feedback,/whoami,/early-adopters/status/{uuid};resolve_registration→UNAVAILABLE; tier →scholar(never elevated); opt-out →processed=false503; Trinity history → refused with no client. Production and local development keep bare names (OAuthEnvironment.account_collectionprefixes only a declared staging); the quarantined coordination store is untouched (excluded by T's contract)scholarwith zero reads; feedback 503 with zero writes;/register503, nopersisted=true, zero writes;/early-adopters/register503, zero writes;/whoami+ status 503, zero reads; opt-outprocessed=false, account untouched, zero I/O; history read[]/ write refused with zero I/O — every one FAILS onfecc67c(production reads/writes observed; 2xx /persisted=truereturned). Declared staging: lookup cannot see the production record and sees its own (sourcestays the logical store name); tier consults onlystaging_early_adopters; feedback on both sites; history in both directions; account status reads only staging (pin). Production + development: bare names for all four bases; staging prefixed (pin)No authentication-gate activation, routing, provider, billing, pricing, release-identity, policy, notice, merge, staging, or deployment semantics changed. Four independent adversarial lenses (real-Firestore transaction semantics; environment-isolation completeness sweep; regression-vacuity audit; production-drift + analyzer exposure) ran read-only against the tree before the commit: four background lens agents were terminated by an API session limit before reporting; two (real-Firestore transaction semantics; regression vacuity) were re-spawned and completed, and two (isolation-completeness sweep; production-drift/analyzer exposure) were executed inline by RNA and are disclosed as non-independent. Lens A HOLDS the library claims (installed client 2.21.0, wrapper diffed byte-identical against 2.30.0: the
transactionalwrapper re-raises the same exception after rollback; the read is sent inside the transaction; Authlib renders the denial as 400 with nothing persisted) and the race claim by invariance (the outcome is the same whether the server aborts the reader, blocks the writer, or ignored the absent-key read) — and REFUTED four things before the SHA: the HIGH re-prefix escalation (its own row above), the reuse-in-window 503, the dropped parent link, and the empty-identity mint; a non-numericexpires_at→ 500 is recorded as a class-wide pre-existing residual. Lens C: no test passes for the wrong reason; the barrier proofs are sound within the disclosed optimistic model; five hardenings taken (the fake now refuses read-after-write like the real client; the recording fake counts transactional I/O; production-caller pins; an outage-stays-outage pin; reason-bound denials plus an unrelated-tombstone over-deny guard) and one receipt corrected (atfecc67cthe opt-out de-identified the production account through the fallback name and then answered "unavailable" — a silent partial mutation, not aprocessed=true). Inline sweep: every Firestore access point inmcp-server/is namespaced or in the excluded coordination store; Starlette's exception-class routing and the wrapper's re-raise verified from the installed sources; no new logged identifiers, empty excepts, or unused imports.Local gates at this head (imports bound to the clone,
verifimind_mcp.__file__printed): unit 1,382 passed / 3 skipped / 1 failed (the one failure is the documented dev-envtest_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority), registration 84 passed, integration 7 passed / 11 skipped; Bandit with CI's flags (-r src/ -ll -ii --skip B101): no issues. Old-head discrimination: the same test file against a sparse worktree atfecc67c(imports bound to the worktree,rev-parseprinted): 33 failed / 11 passed — every "fecc67c:" test fails for its stated reason (log bound in the S160 record), every "pin:" test passes.Remote checks at
2c43cd532790a6bf5d5dee184cd3898db2ff8dea(read from the commit's check-suite after every expected name existed and every run completed, including thegithub-advanced-securityCodeQLresults check):Bandit SAST Analysis(posted twice)Security Scanworkflow, run 33949772734 — the real scan, advisory (|| true); andDocs Contract and CI Bypass, run 33949772730 — synthetic instant-passSafety Dependency CheckSecurity Scan— advisory (|| true)Security ScanTest Suite, run 33949772731Production Image Dependency ParityTest Suite— builds the Python 3.12 image and asserts the pins inside itRun TestsTest Suite— Python 3.11: unit + registration + integrationServer Health CheckTest SuiteSonarCloud Code AnalysisCodeQL AnalysisSecurity Scan— scan + SARIF upload (security-and-quality)CodeQL10 check-runs / 9 unique contexts, non-success 0; PR
mergeStateStatus: CLEAN. Read at 06:27:10Z on 2026-09-05 after every expected name existed and every run had completed.CodeQL(github-advanced-security) at this head: success — title "8 new alerts", summary "8 notes": the same eight pre-existing notes dispositioned in the T S156 section (annotations 8/8; two line numbers shifted by this diff), 0 error-level. Open code-scanning alerts for pr=346 by rule: 2×py/cyclic-import, 1×py/unused-global-variable, 5×py/unused-import; 0×py/uninitialized-local-variable. Code-scanning analysis onrefs/pull/346/merge: 2026-09-05T06:26:41Z, merge shab192aedbe, results=8 (unchanged fromfecc67c'sa1156a5dd).Evidence currency (T WP-C).
d021f8cwas the intermediate S159 head (9/10 — results check red);fecc67cis the final green S159 head and the parent of this commit. The merged S159 handoff'sd021f8ccurrent/green lines and the original CS prompt's2f44433pins are preserved as history and corrected prospectively in the S160 close; the round-4 CS prompt is pinned only to2c43cd532790a6bf5d5dee184cd3898db2ff8dea.CS round 6 (2026-09-08, T S159) → bounded erasure-seal / receipt-truth repair (head
59424c57406cbe536cf03565edef310024f7167f)CS-protocol round 6 — executed by a fresh Codex subagent on T's platform per Alton's standing seat decision and adjudicated separately by T (
.macp/reviews/20260908_T_pr346_cs_round6_adjudication_exact_pair_rebind.md; artifact SHA-256689D9A8E…) — accepted the S162 repair on its own terms (F-01 refresh containment holds under every tested replay, wrong-secret, outage, descendant, PAT and warm-cache ordering; the transaction-protected F-02 cases hold; the 24-test parent differential is genuine at 18 fail / 6 pass) and returned BOUNDED-HOLD / NOT SAFE TO STAGE on three narrower TERMINAL-STATE defects: the durable post-state at the failure and concurrency boundaries, which green tests had not asserted. Alton activated T's bounded repair; this head is one child commit of84fd9265701a38766bb6d3d8aa3cc3a0de77745b.tests/unit/test_cs_round5_identity_availability.py(36 tests; 10 FAIL on84fd926/ 26 pass)processed=True,tombstoned=True,claim_names_erased_subject=True) and reproduced independently before designing: the legacy read, the tombstone check and the claim write were THREE separate operations in both registration lanes and the verified resolver, and opt-out's reverse-query claim sweep is a snapshot that cannot exclude a later writer84fd926: the claim survived in both); the seal refuses claim writers while the caller's real PAT still validates and the subject is not yet tombstoned (pin on the two-marker split)status=503,persisted=False,accounts=1,stored_emailandstored_registration_feedbackpresent): the feedbackadd()was the LAST write inside the fatal boundary, so its failure returned "no account was created" while the account carrying the address and that feedback text was committedfeedback_received=false— truthful, and identical for a new and an existing address, so it discloses nothing. Every remaining 503 therefore comes from the claim or account write, which makes the receipt's absence claim provable rather than assertedfeedback_received=false, the account present and no feedback document added (84fd926: 503 with the account stored); a full storage outage still returns the honest 503 with the absent account ASSERTED, in both lanes (pin)processed=False,subject_tombstone_committed=True,bearer_valid_after_recovery=False): success after a failed hygiene sweep depended on an IMMEDIATE confirmation read, and when that read failed too the caller was told the erasure had not happened and to retry with a bearer the tombstone had already killedprocessed=truewith the claim released, PII scrubbed and the bearer dead (84fd926:processed=false); a tombstone write that fails is never reported as success and still names the private channel (pin on the ambiguity half)Also corrected: the test fake gated each logical write twice (T's R6-05);
create()/update()now pass the gate exactly once, as one commit RPC would, which is what makes the new single-owners-write assertion a sound oracle. Deferred by T's instruction and NOT repaired: R6-04,_count_tier_slots()converting an aggregate-count exception to0so a count-only failure can exceed the cohort cap — an availability/data-integrity issue with no staging credential or privacy effect, recorded for its own bounded lane.Three commits, and why — a disclosed deviation from the one-SHA instruction. The first commit
cedc24b52dd282615525f9c4f7460b31db500568introduced the erasure seal by WIDENING one shared predicate (_subject_revoked→ seal or tombstone). RNA's own adversarial lens, run against that head before it was handed to review, refuted it: the widened predicate also gated the resolver's decision that a mailbox is free again, which is a different question. An erasure interrupted after the seal — its claim release failed, so its caller keeps a live bearer and is expected to retry — then looked revoked to that branch, and the next sign-in minted a FRESH subject with a new active account carrying the address in clear while stranding the original: never tombstoned, credentials alive, unreachable from the owner route. At the round-6 parent that same state failed closed, so it was a HIGH self-inflicted regression, reproducible with no attacker and only an outage. The corrective commit splits the predicate:_subject_revokedkeeps seal-or-tombstone and answers "may I bind new state to this subject?" (the transactional claim guard and the legacy-backfill refusal); the new_subject_erasure_completeis tombstone-only and answers "is this mailbox free again?" — the only predicate that may trigger a reclaim. An unfinished erasure now fails closed with an explicit log and the caller finishes it with the bearer that state deliberately leaves alive. Three LOW items landed with it:write_subject_tombstone("")no longer returns success for a marker the store silently drops (the exact contract R6-03 rests on),subject_is_sealed_or_revokedno longer falls open on an empty identifier, and the hygiene-sweep handler no longer swallows programming errors behind a backend warning. A THIRD commit59424c57406cbe536cf03565edef310024f7167fthen followed, because a second lens found false claims in the regression file's own comments: apin:contract that three tests break at the parent (they fail there on symbol absence, which proves an addition rather than a behaviour), a differential characterised as "six behavioural" when it is five, one test whose only assertion a bare uncaught 500 satisfied, and a helper docstring claiming "every field" while skipping one — plus four mutants that no test in the suite killed. All are corrected and all four mutants closed (each verified to fail with its mutation applied); tests and comments only, no source change. The repair is therefore one bounded change delivered in three commits; handing the independent seat a head carrying a known HIGH regression, or evidence whose comments misdescribe it, was judged the worse option. The regression discriminator fails atcedc24bwith "an unfinished erasure handed the mailbox to a fresh subject".Re-disclosed rather than certified: the account write exists only on the new-address branch, so an account-collection-only outage still separates new from existing (201 vs 503) in both lanes. The lens confirms this is identical at the round-6 parent — pre-existing residue, already on T's own deferred list — but
cedc24b's framing ("every remaining 503 comes from the claim or account write") asserted it away. It is a residual, not a closed item.Disclosed residuals: the erasure seal is permanent, like every marker in that collection — a mailbox is re-registerable after erasure (the address gets a fresh subject) but that subject identifier is sealed forever, which belongs on the TTL lane; honouring the seal in
_subject_revokedmakes a sealed-but-not-yet-tombstoned subject un-adoptable and un-healable a beat earlier than before (deliberate — erasure has begun); the account-collection-only outage asymmetry and the new-versus-existing commit-timing difference are unchanged from round 5.No authentication-gate activation, routing, provider, billing, pricing, release-identity, policy, notice, merge, staging, or deployment semantics changed; anonymous feedback remains admitted while issuance is dark. The user-visible behaviour changes are exactly three: a feedback-store failure no longer fails the registration, an erasure whose hygiene sweep fails is reported as processed rather than unconfirmed, and a claim naming a subject under erasure is refused. Adversarial lenses before the commit: two adversarial lenses ran read-only against the pushed head in separate agent contexts, before it was handed to review. Lens A (identity/lifecycle refutation, attacker position) enumerated every writer into the owners collection and confirmed the seal machinery is built rather than asserted — every existing-subject claim write really does read both markers through the transaction that writes it, verified under BOTH the fake's optimistic model and real server-client read locks; the seal is invisible to
validate_bearer,validate_refresh, rotation and_is_tombstoned; the fresh-mint paths cannot be driven onto an existing subject; and seal-as-denial-of-service is closed by the owner-scoped opt-out route (anonymous and wrong-subject both 401 with zero markers written). It graded R6-03 HOLDS and REFUTED part of R6-01: the widened predicate reached the resolver's mailbox-is-free decision, forking an interrupted erasure into a fresh subject and stranding the original — the HIGH regression the corrective commit closes, reproduced independently by a discriminating test before the fix. It also found the empty-identifier fail-open, the empty-key tombstone contract, and the over-broad sweep handler (all closed), and confirmed the account-write asymmetry as pre-existing at the parent rather than new. Lens B (regression vacuity, mutation harness) audited the suite against a 16-mutant catalogue and the old-head reason table; its report is recorded in the S163 record.Local gates at this head (imports bound to the clone,
verifimind_mcp.__file__printed; on WindowsPYTHONPATHuses;and pytest's temp root must be short): required targeted set + this file 242 passed — all 230 round-6 controls retained plus the 12 new tests; unit 1,457 passed / 3 skipped / 1 failed (the one failure is the documented dev-envtest_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority); integration 7 passed / 11 skipped; Bandit on the changed sources: no new findings. Old-head discrimination: the same file, with the changed fake, against a detached worktree at84fd926: 10 failed / 26 passed — SEVEN fail behaviourally and THREE fail because store primitives this repair introduces do not exist at the parent; a symbol-absence failure proves an addition, not a behaviour, so the split is stated rather than presented as ten behavioural.Remote checks at
59424c57406cbe536cf03565edef310024f7167f(read from the commit's check-suite after every expected name existed and every run completed, including thegithub-advanced-securityCodeQLresults check):Bandit SAST Analysis(posted twice)Security Scanworkflow, run 34252456327 — the real scan, advisory (|| true); andDocs Contract and CI Bypass, run 34252456337 — synthetic instant-passSafety Dependency CheckSecurity Scan— advisory (|| true)Production Image Dependency ParityTest Suite, run 34252456156 — builds the Python 3.12 image and asserts the pins inside itSecurity ScanTest SuiteSonarCloud Code AnalysisRun TestsTest Suite— Python 3.11: unit + registration + integrationServer Health CheckTest SuiteCodeQLCodeQL AnalysisSecurity Scan— scan + SARIF upload (security-and-quality)10 check-runs / 9 unique contexts, non-success 0; PR
mergeStateStatus: CLEAN, draft. Read at 16:41:43Z on 2026-09-08 (UTC; 2026-09-09 00:41 +08:00) after every expected name existed and every run had completed.CodeQL(github-advanced-security) at this head: success — title "13 new alerts", summary "13 notes", 0 error-level and 0 in production logic: 6×py/cyclic-importon the established lazyregistration ↔ oauth.storesimports (the pattern that AVOIDS a real import cycle — this repair added call sites, so the count rose from 2 at68daa5cto 6), 5×py/unused-importand 2×py/unused-global-variable(one of them_WRITE_OPS, dead in the test file since the owners-write assertion was re-instrumented at the commit gate). The four test-style notes disclosed at84fd926are CLEARED. All cosmetic; disclosed and deferred to the next bounded SHA or T's residual list rather than spent on a third commit. Code-scanning analysis onrefs/pull/346/merge: 2026-09-08T16:41:24Z, merge sha7c6211305, results=13; 0×py/uninitialized-local-variable.CS round 5 (2026-09-06, T S159) → bounded lifecycle/availability repair (head
84fd9265701a38766bb6d3d8aa3cc3a0de77745b)CS-protocol round 5 — executed by a fresh Codex subagent on T's platform per Alton's standing seat decision and adjudicated separately by T (
.macp/reviews/20260906_T_pr346_cs_round5_adjudication_exact_pair_rebind.md; artifact SHA-25654DD7BC5…) — reproduced every S161 receipt (206 targeted, 1,423 / 2 unit, the exact 32 / 7 old-head differential; the S161 repair materially closes the four round-4 traces) and returned BOUNDED-HOLD / NOT SAFE TO STAGE on four narrower orderings in the lifecycle of the credential family and the ownership claim. Alton activated T's bounded repair; this head is one child commit of68daa5c189d758bd1da2edb63cf42b25140ae2d4.tests/unit/test_cs_round5_identity_availability.py(24 tests; 18 FAIL on68daa5cat their stated first assertion / 6 pins pass on both heads)SAVE_TIME_RECORD_IS_NONE True,FAMILY_TOMBSTONED False,WINNER_ACCESS_LIVE True):save_token's secondvalidate_refreshreturned None for a token rotated after grant-authentication and raisedinvalid_grantwithout callingcontain_refresh_reusecontain_refresh_reuse(presented)revokes the grant family beforeinvalid_grant. Containment fires only for a secret-valid, already-rotated token (a wrong-secret or unknown token revokes nothing); an outage on either guarded read raisesStoreUnavailableand propagates, never collapsing into a denialinvalid_grantAND the winner's access and refresh are dead — with a known-positive that they were live an instant earlier (68daa5c: both live); an outage INSIDE containment propagates asStoreUnavailable(68daa5c: no containment ran); an outage raised by the re-validation itself propagates (pin)ACCOUNT_COUNT 2): the resolver released a tombstoned owner claim with a baredelete(), so two verified ceremonies each erased the other's replacement claim and wrote separate accounts_reclaim_tombstoned_ownerreads the claim and installs the fresh subject in ONE transaction, bailing (re-read, adopt the winner) if the claim no longer names the tombstoned uuid it read; the resolver mints its fresh verified subject inline through it, create-if-absent;_release_owner_claimdeletes only while the claim still names the expected subject. Read-set conflicts (fake) or server read locks (real client) serialize competing writers68daa5c: two accounts); a resolver interposed inside the reclaim's read→commit window is conflicted and adopts (68daa5c: two accounts); opt-out leaves a claim re-pointed to another subject alone, via the public path (68daa5c: erased) and when the re-point lands inside the release's own commit window (68daa5c: erased); the reclaimed claim is unverified until its record lands (pin)BEARER_LIVE_AFTER_FIRST False,CLAIM_AFTER_FIRST True,SECOND_DIRECT_PROCESSED True,CLAIM_AFTER_SECOND True): PII scrub and credential revocation ran BEFORE the claim delete, so a delete failure left the caller revoked (401 on retry), the claim present, the email key gone, and a later retry reporting success while the claim remaineduuidfield (a durable cleanup identity that survives the scrub and also cleans the inherited pre-repair state — claim present, email already scrubbed — the parent could never clean) → tombstone + revoke LAST. A release failure leaves the caller's real bearer valid and the claim findable for the retry; once the subject tombstone every validation path consults has landed, a failed hygiene sweep no longer denies an erasure that happened. Scrubbing before releasing closes the release-to-tombstone window: a ceremony there finds no email and mints a FRESH subject68daa5c: bearer dead); a retry after the outage clears returnsprocessed=trueonly with the claim gone (68daa5c:truewith the claim present); an inherited scrubbed record with a stale claim is cleaned by uuid (68daa5c: never); a sweep failure after the tombstone reports truthful success with the bearer dead (68daa5c:false); a scrub failure before the release is resumable with the same credential (pin); a clean opt-out still releases, scrubs, tombstones (pin)EA_EXISTING 201/EA_NEW 500 text/plain;LIGHT_EXISTING 200/LIGHT_NEW 500): an existing address performed no write while a new address hitclaim_email().create()and the backend failure escaped as HTTP 500email_hashre-asserted (nothing new stored) — so a storage outage fails both identically, and both lanes then raiseRegistrationStoreUnavailable, rendered by the handlers as ONE retryable 503 (persisted:false,uuid:"", "could not be completed and no account was created",Retry-After: 60,Cache-Control: no-store). Never a generic 500 (oracle), never a success screen (F-RES-1): a persistent quota or permission failure is loud. The client-construction (db is None) branch, slot-cap, environment-identity, validation errors, and the anonymous lightweight path are outside the boundary and unchanged; a non-backend exception still propagatespersisted:false, no phantom identifier (68daa5c: 500 vs 201 / 500 vs 200); a quota exhaustion is never a silent success in either lane and persists nothing (68daa5c: 500); an existing address performs exactly one owners write —updatewith a claim,createfor a legacy record (68daa5c: none); a legacy record's claim is backfilled by the lane (68daa5c: no claim); feedback-store-only and reads-down outages return the same honest receipt for both addresses (68daa5c: 500); a record-store-only failure yields the honest receipt (68daa5c: 500); the anonymous path keeps its UUID and hands out none on a write outage (pins)Disclosed residuals, not scored: (a) a failure confined to the ACCOUNT collection while the owners collection accepts writes is asymmetric by construction (an existing address never writes its account record on this path) — not a realistic Firestore failure mode, since transport, quota, and IAM failures are project-wide; (b) the read/write timing difference between new and existing addresses (the client retries a failing commit for up to ~60 s) is named, as round 5 named it; (c) an existing address now costs one small owners write per duplicate submission, bounded by the outer rate limiter exactly as a new-address flood is; (d) the lanes now backfill a legacy record's missing claim — the verified resolver's own rule applied one step earlier, a behaviour change T should see plainly.
No authentication-gate activation, routing, provider, billing, pricing, release-identity, policy, notice, merge, staging, or deployment semantics changed; anonymous feedback remains admitted while issuance is dark; the
db is Nonebranch keeps its ratified F-RES-1 shape. Adversarial lenses before the commit: two adversarial lenses ran read-only against the draft tree before the commit, in separate agent contexts. Lens A (identity/lifecycle refutation, attacker position; 24 probes outside the tree, 24/24 on the draft vs 14 failing at the parent) held F-01, F-02, and F-03 across the optimistic-fake and pessimistic-lock models, warm-cache and PAT descendants, and every opt-out failure point — and REFUTED the first F-04 draft (MEDIUM): it answered the oracle by returning the uniform SUCCESS receipt on a write outage, claiming persistence that never occurred and turning quota exhaustion or permission misconfiguration into silent success on every request (a reversal of the ratified F-RES-1 doctrine); it also found two LOW F-03 residuals (a sweep failure after the tombstone reported failure with a dead bearer; a claim re-created by a ceremony in the release window with no reverse lookup) and two hygiene items (a deaddbparameter; the fake raisingKeyErrorwhere production raisesNotFound). All redesigned before the SHA: symmetric owners write + honest 503; scrub → reverse-lookup release → tombstone-authoritative success; hygiene closed. Lens B (regression vacuity, mutation harness): on the first suite 10/14 mutants killed, two old-head failures incidental (a missing symbol; a re-raised server exception), real oracle shapes hidden by field-subset receipts, an F-01 pin injected upstream of its branch, a tombstone read standing in for "bearer alive"; thirteen verified candidate tests handed over. The suite was rewritten — whole-body and header-key receipts, a status-not-exception client, known-positives inside the F-01 patch, a real PAT as the retry-credential proxy, the candidates, and the redesign's own discriminators — and the final 18-mutant matrix on the committed code has no survivor (the one first-pass survivor, an unconditional transactional release unreachable through the reverse lookup alone, is killed by re-pointing the claim inside the release's commit window).Local gates at this head (imports bound to the clone,
verifimind_mcp.__file__printed; on WindowsPYTHONPATHuses;and pytest's temp root must be short — a deep redirectedTEMPproduces long-path false failures in an unrelated LLM-failover file, reproduced identically at the clean parent): unit 1,445 passed / 3 skipped / 1 failed (the one failure is the documented dev-envtest_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority), required targeted set + round-4 controls 206 passed + this file 24 passed, integration 7 passed / 11 skipped; Bandit on the changed sources: no new findings (one pre-existing Low B105 on the literal"Bearer"); mutation matrix on the final code 18/18 killed. Old-head discrimination: the same file (and the updated fake) against a detached worktree at68daa5c(rev-parseprinted, helpers confirmed absent in the old source): 18 failed / 6 passed — every "68daa5c:" test fails at its stated first assertion, every "pin:" test passes (log bound in the S162 record).Remote checks at
84fd9265701a38766bb6d3d8aa3cc3a0de77745b(read from the commit's check-suite after every expected name existed and every run completed, including thegithub-advanced-securityCodeQLresults check):Bandit SAST Analysis(posted twice)Security Scanworkflow, run 34067551697 — the real scan, advisory (|| true); andDocs Contract and CI Bypass, run 34067551635 — synthetic instant-passSafety Dependency CheckSecurity Scan— advisory (|| true)Production Image Dependency ParityTest Suite, run 34067551657 — builds the Python 3.12 image and asserts the pins inside itSonarCloud Code AnalysisRun TestsTest Suite— Python 3.11: unit + registration + integrationCodeQLCodeQL AnalysisSecurity Scan— scan + SARIF upload (security-and-quality)Server Health CheckTest SuiteSecurity ScanTest Suite10 check-runs / 9 unique contexts, non-success 0; PR
mergeStateStatus: CLEAN. Read at 23:42:07Z on 2026-09-06 (UTC; 2026-09-07 07:42 +08:00) after every expected name existed and every run had completed.CodeQL(github-advanced-security) at this head: success — title "10 new alerts", summary "10 notes", 0 error-level, 0 in production code: the six pre-existing dispositioned notes (1×py/unused-global-variable, 5×py/unused-import) plus four new test-style notes, all intests/unit/test_cs_round5_identity_availability.py— a reads-down fake's__getitem__raising the outage class instead of aLookupError(py/unexpected-raise-in-special-method), the thread runner catchingBaseException(py/catch-base-exception), and two chained== … is not Nonecomparisons (py/test-equals-none×2). They became visible only after the push; the one-SHA contract forbids a cosmetic follow-up commit, so they are disclosed here and deferred to the next bounded SHA (or T's residual list). Code-scanning analysis onrefs/pull/346/merge: 2026-09-06T23:41:46Z, merge sha1bdf4e2a8, results=10 (was 6 at68daa5c's0da13bbb2); 0×py/uninitialized-local-variable.CS round 4 (2026-09-05, T S158) → bounded identity/provenance repair (head
68daa5c189d758bd1da2edb63cf42b25140ae2d4)CS-protocol round 4 — executed by a fresh Codex subagent on T's platform per Alton's seat decision and adjudicated separately by T (
.macp/reviews/20260905_T_pr346_cs_round4_adjudication_exact_pair_rebind.md; artifact SHA-25639E32698…) — reproduced every S160 receipt (167 targeted, 1,382 / 3 / 1 unit, 33 / 11 old-head differential; WP-A and WP-B materially hold) and returned BOUNDED-HOLD / NOT SAFE TO STAGE on a new, narrower class: unauthenticated state acquiring verified-subject meaning. Alton activated T's bounded repair; this head is one child commit of2c43cd532790a6bf5d5dee184cd3898db2ff8dea.tests/unit/test_cs_round4_identity.py(39 tests; 32 FAIL on2c43cd5at their stated first assertion / 7 pins pass on both heads)adopted_updates_consent: True,linked_feedback_survives_adoption: True): adoption wiped name and inline feedback only; both lanes were query-then-write in two different collections, so one address could own two accounts and the resolver adopted whichever it scanned firstemail_owners, environment-resolved — bare in production,staging_in staging) created with Firestore's atomicDocumentReference.create(), which exactly one caller can ever win; both lanes first scan BOTH account collections for a legacy record, then claim; the verified resolver adopts through the claim, backfills legacy records into claims, and heals a claim whose record write never landed. Mailbox proof is a state-transition sanitizer, not retroactive authorship: adoption neutralizes every caller-chosen field (profile, inline feedback,updates_consent→ off, tier → lane default,pilot_source), stamps the verified actor's OWN Terms/Privacy acceptance andconsent_source, records what it neutralized, and detaches feedback linked before proof. Registration-path feedback is now written withuuid=Noneand an explicitly unverified link2c43cd5: two); a legacy record in the OTHER lane blocks a new account (2c43cd5: did not); a claimed address is owned before its record is visible (2c43cd5: no claim concept); the ceremony creates one subject AND its claim, backfills a legacy record, heals a claim without a record under the claimed identifier, canonical key (2c43cd5: none of these existed); T's exact probe — consent, tier, pilot provenance, profile neutralized, ceremony-time acceptance,consent_source(2c43cd5:updates_consentsurvived); linked feedback never carries the subject (2c43cd5:uuid=<victim>); legacy linked feedback detached (2c43cd5: survived); verified-bearer feedback untouched, an already-verified subject unchanged, a deletion-requested record never adopted (pins)anonymous_claimed_uuid_persisted: True): the bodyuuidwas written into the authoritative field with no subject bindinghttp_server._bearer_subject):uuidis the bearer subject or None,attributionnames the grade, a body UUID is kept only asclaimed_uuid_unverified; a Bearer that is present but invalid → 401 with nothing stored; credential-store outage → 503 with nothing stored; anonymous feedback stays admitted (T D-157-5)uuid None, claim recorded (2c43cd5: body UUID stored); bearer of subject A + body UUID B →uuid == A, B kept only as a claim (2c43cd5: B stored); invalid bearer → 401, nothing stored (2c43cd5: 201); outage → 503no-store, nothing stored (2c43cd5: 201); anonymous without a claim still 201 (pin)ea_messages_equal: False,lightweight_messages_equal: False): the duplicate branch said "If an account already exists…" withfeedback_received=False, the new branch "Thanks — your interest is recorded…"_UNIFORM_EMAIL_MESSAGE,_uniform_lightweight_response); the EA lane records feedback for both cases, explicitly unverified, sofeedback_receivedis truthful and identical; the slot-cap and storage-down branches were already existence-independent2c43cd5: bodies differ); lightweight lane likewise (2c43cd5: differ); the anonymous lightweight path still returns its UUID (pin)owner_dashboard_auth_outage_status: 401,owner_optout_auth_outage_status: 401):_authenticated_subject's broadexceptswallowedStoreUnavailable_bearer_subjectclassifies absent / invalid / ok and PROPAGATESStoreUnavailable(andEnvironmentMisconfigured); dashboard → 503 page with no history read; opt-out → the existing non-enumeratingprocessed=false503 with no deletion attempted; both carryRetry-AfterandCache-Control: no-storeread_trinity_historynot called (2c43cd5: 401); opt-out outage → 503processed=false,process_optoutnot awaited (2c43cd5: 401); missing / invalid / wrong-subject credentials stay 401 on both routes (pin)Closed before the SHA by the S161 adversarial lenses (each with a parent-failing regression): (a) a regression of this repair's own first draft — opt-out did not release the ownership claim, so an opted-out mailbox could never register again and, after a purge, the heal path would have re-created the address under the subject-tombstoned identifier; now opt-out releases the claim and the resolver never adopts or heals a tombstoned subject (it releases the stale claim and mints fresh); (b) lane record writes were unconditional
set(), so a record the ceremony healed between the lane's claim and its write could be clobbered with the unverified preregistration — lane and heal records are now create-if-absent; (c) anyearly_adoptersdocument, verified or not, was granted the pioneer rate bucket — an explicitly unverified preregistration now earns none (legacy records without the flag keep their standing); (d)registered_atnow reflects the verified actor's moment (the earlier value kept as provenance), corrupt claims fail closed, and no broadexceptturns programming errors into "invalid credential". Also closed in passing: the round-4 residual that the verified resolver constructed the Firestore client before resolving its collections (it now resolves first, like every other user-data path). The other round-4 residuals — raw UUID in two warning-log paths, per-subject rate buckets rotated by unauthenticated UUID headers with process-local caps, single-chunk body-peek retention, DCR echo bounds, corruptexpires_at— are unchanged and stay routed to the staging matrix and backlog exactly as T recorded them.No authentication-gate activation, routing, provider, billing, pricing, release-identity, policy, notice, merge, staging, or deployment semantics changed; anonymous feedback remains admitted while issuance is dark. Adversarial lenses before the commit: two adversarial lenses ran read-only against the tree before the commit, in separate agent contexts. Lens A (identity/provenance refutation) held contracts 3–5 and the regression envelope, verified
create()'sPrecondition(exists=False)→AlreadyExistsfrom the installed client, and REFUTED four things in the first draft — the opt-out claim retention (HIGH), the heal-vs-set()clobber (MEDIUM), unverified cohort privilege (MEDIUM), andregistered_at/ fail-open corrupt claims / the broadexcept(LOW) — all repaired before the SHA (its own full-suite observation: 1,395 passed / 3 skipped with the parity module deselected). Lens B (regression vacuity, mutation harness): 24 of 27 mutations of the repaired code were killed by the round-4 file and every old-head reason matched; it flagged a mislabelled pin, a timestamp-echo oracle hidden by the frozen clock, the resolver's dead lost-race path, the unpinned atomiccreate, and unasserted sanitizer fields — all folded in (clock advanced between the two requests with the second receipt required to carry the current time; two interleaving tests; a one-atomic-create-and-nothing-else pin; tier / consent / whole-record / claim-path-detach / lightweight-lane assertions; which-503 pinned by body and headers).Local gates at this head (imports bound to the clone,
verifimind_mcp.__file__printed): unit 1,421 passed / 3 skipped / 1 failed (the one failure is the documented dev-envtest_exact_pins_are_the_versions_actually_installed; CI installs the pins and is the authority), registration 84 passed, integration 7 passed / 11 skipped; Bandit with CI's flags (-r src/ -ll -ii --skip B101): no issues. Old-head discrimination: the same file against a sparse worktree at2c43cd5(imports bound to the worktree,rev-parseprinted): 32 failed / 7 passed — every "2c43cd5:" test fails at its stated first assertion, every "pin:" test passes (log bound in the S161 record).Remote checks at
68daa5c189d758bd1da2edb63cf42b25140ae2d4(read from the commit's check-suite after every expected name existed and every run completed, including thegithub-advanced-securityCodeQLresults check):Bandit SAST Analysis(posted twice)Security Scanworkflow, run 33998533443 — the real scan, advisory (|| true); andDocs Contract and CI Bypass, run 33998533461 — synthetic instant-passSafety Dependency CheckSecurity Scan— advisory (|| true)Security ScanTest Suite, run 33998533415Production Image Dependency ParityTest Suite— builds the Python 3.12 image and asserts the pins inside itRun TestsTest Suite— Python 3.11: unit + registration + integrationServer Health CheckTest SuiteSonarCloud Code AnalysisCodeQL AnalysisSecurity Scan— scan + SARIF upload (security-and-quality)CodeQL10 check-runs / 9 unique contexts, non-success 0; PR
mergeStateStatus: CLEAN. Read at 23:24:41Z on 2026-09-05 (UTC; 2026-09-06 07:24 +08:00) after every expected name existed and every run had completed.CodeQL(github-advanced-security) at this head: success — title "6 new alerts", summary "6 notes": six of the eight pre-existing dispositioned notes (1×py/unused-global-variable, 5×py/unused-import); the twopy/cyclic-importnotes no longer appear in the merge-ref analysis; 0 error-level; 0×py/uninitialized-local-variable. Code-scanning analysis onrefs/pull/346/merge: 2026-09-05T23:24:27Z, merge sha0da13bbb2, results=6 (was 8 at2c43cd5'sb192aedbe).Evidence at
2f44433/7a0c462(history, unchanged)Local at
7a0c462: 1,351 passed / 3 skipped across unit + registration (1,267 + 84). Full end-to-end OAuth ceremony verified through the real ASGI app (DCR → authorize → verify → consent → token → bearer at /mcp; 401 unauth, 403 insufficient-scope).Deferred lanes (T's route, not silently omitted)
CIMD with SSRF-hardened metadata retrieval and the SDK-backed MCP
2026-07-28implementation remain after CS pass 1 and the exact-client2025-11-25matrix. Physical-deletion (TTL) sweep receipts are a retention follow-up. Before the client matrix relies on per-IP mint limits, staging must bind the_client_iptrusted-last-hop assumption to the actual ingress topology. Release-lane follow-up surfaced by the license lens:.dockerignoreexcludes*.md, soTHIRD_PARTY_NOTICES.mdis not copied into the production image (the upstream license files ARE shipped at<dist>.dist-info/licenses/byuv pip install); adding!THIRD_PARTY_NOTICES.mdis a deployment-artifact change and is deliberately not made in this bounded SHA. No claim is made that those gates are complete.Deploy-time env (for the eventual staging/dark deploy — not this SHA)
VERIFIMIND_ENVIRONMENTandVERIFIMIND_PUBLIC_ORIGIN(a staging service MUST declare both — since this head an unresolvable identity refuses every user-data surface with an explicit 503 instead of falling back to production collection names),OAUTH_ISSUANCE_ENABLED(default off),REGISTRATION_GATE_ENABLED(default off),AUTH_BOUNDARY_MODE(default connection),INGRESS_PROXY_HOPS(default 1 — the element the Google Front End appends last; set 2 only behind a Google external Application LB, proven by the deployment's ingress receipt),OAUTH_HASH_PEPPER,VALUE_SUBJECT_HMAC_KEY,SMTP_*(Brevo, via Secret Manager),MAIL_RECIPIENT_ALLOWLIST(staging). Policy publication/gate dates are still identity-time fills (dates only).Chain from here
Only with every remote check green at this exact SHA: fresh CS-protocol safe-to-stage round 7 of
59424c57406cbe536cf03565edef310024f7167f— executor per Alton's standing seat decision (a T (CTO) Codex subagent, adjudicated by T's main session and named by that evidence grade), new exact-head prompt only → T exact-pair rebind → Alton's separate isolated-staging decision (bindingINGRESS_PROXY_HOPSto the staging service's own ingress by the same receipt; the staging service must declareVERIFIMIND_ENVIRONMENT=stagingAND its ownVERIFIMIND_PUBLIC_ORIGIN, or every user-data surface refuses with an explicit 503 by design) → dark client matrix → SSRF-CIMD + 2026-era lane → CS final → T exact-head rebind → Alton's separate policy/notice/activation decisions.🤖 Generated with Claude Code
https://claude.ai/code/session_01WebraCcrWMdTm1y1PvWsVU