App secret plus the member's live bearer for that app, a per-profile grant and method allowlist (bifrost only), a nonce spent once per session through the existing oauth_state_uses claim (no schema change), an in-process fixed-window rate per account and per app, and an audit row per assertion or post-secret refusal. Provider step-up sign-ins now label their proof provider:<name>.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
resolveBearer is the one bearer verifier; the link race maps to link_initiator_mismatch and links/confirm 409.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The 1500 ms bound measured 3330 ms under parallel load. What the test proves is that
a decompression bomb is read under the 16 KB cap; record the inflate options and assert
maxOutputLength directly. Mutant (drop the cap) fails the test.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Not in the 74004bf release list. The runtime role needs GRANT SELECT, DELETE ON oauth_state_uses before this build starts.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
accepted_at is origin_server_ts, received_at is the collector's clock; a collector clock behind the homeserver's excludes a first answer. Fails closed.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A provider that keeps its refresh token valid (Twitch) keeps it when the
rotation cannot be committed: the marker is left, the token stays, and the
next caller after the backoff presents it. Blanking stays for a revoke and
for a provider that rotates (the rotate decision now carries
consumedRefreshToken). Live-Postgres test for Twitch written first and seen
failing; the contract states the rule inline.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
When the rotation or revocation the provider's answer called for cannot be
committed, the fallback write now clears refresh_token_encrypted as well as
stamping the marker, so the end of the 60 s backoff cannot replay the consumed
token. Tests (live Postgres, trigger-refused revoke) assert no replayable token
and no second provider call. Docs: the marker is a best-effort write on a
second connection after rollback, the skew cap, the blanking write's residual.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A rotating provider that answered without a new refresh token has consumed the old one; the revoke
that follows is rolled back if its UPDATE fails, and no marker remained. The fallback marker now
covers revoke as well as rotate. The residual-risk paragraph says the window opens when the request
is sent, cites the steward ruling, and ends its sentences.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The marker carried the time the attempt began, so a slow failure ate the backoff; it now carries the
time the provider's answer (or failure) was known. A throw after the provider answered (custody
encrypt) records the marker instead of rolling it back, and a rotation Postgres refuses to write
leaves the marker in a transaction of its own. A stamp from a writer's clock ahead of the reader's by
the backoff or more is treated as skew, not a fresh failure.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A failed or timed-out refresh attempt now writes refresh_failed_at under the row lock; a process
that takes the lock inside a 60 s backoff answers managed_credential_refresh_failed without calling
the provider. Rotation, revocation and re-link clear the marker. Closes the cross-process residual
that refresh-once-per-token left to a follow-up, and pins it on the two-pool rig.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The consent_refused page's return link is tested for heimdall_status,
heimdall_error, heimdall_refused_by and no completion code, for both a decline
and a provider access_denied. A browser_completion refusal now carries
heimdall_attempt_id. The contract for app authors is in the consent doc.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The ask sets its own cookie and no launch binding; Continue sets the binding
and carries the exact text shown into it; the receipt stores that text and its
hash. Grants-page form tokens keep the link's expiry. Revocations are unique
per grant and revoke reports only what it wrote. access_denied's attempt
denial code is pinned for Bifrost and Repixelizer.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Managed custody (callbackMint custody_only) now stops at a Heimdall page before
the provider redirect. A decline or a provider access_denied is consent_refused
(by viewer|provider), distinct from provider_error. A granted completion writes
an append-only consent receipt with the asked and granted scopes and the digest
of the wording shown. GET /v1/me/grants lists every grant and receipt of the
signed-in account; revoking one strips the credential, so the app's next token
request is refused, and writes a revocation receipt.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Room notice version is a JSON number (a string, including "01", is malformed);
the answer key keeps the version as encoded text under the leading-zero
refusal. Same rule, two encodings. The registration checkbox records no
version: recorded as an open gap Void is raising.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fold the leading-zero text into one version rule under Representation:
sample, notice, notice acceptance and terms versions, and the room notice,
all positive decimal integers, no leading zeros, at most 4294967295.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Leading-zero versions are refused, not normalised; one version, one key.
A room's notice is the org.gamecult.notice state event {version, scope}
under the room's power levels; a pinned message is not accepted. Absent
or malformed means no train line and the narrowest scope.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Scopeless replies are stored and excluded; tombstone is HMAC-SHA-256 with a
member-side salt; representation, training-rule scope match, scope partition
and MemberTerms ownership fixed. Docs only.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Room notice declares scope (train included); first reply accepts a notice
version; terms version for newcomers only; training needs an acceptance at
answer time and a recorded scope; redaction is exit, past use not undone;
salted hash-only tombstone; no reward sound on consent controls; personal
details out of scope; typed field tables for the CultCache records.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>