fix: prewarm and profile hydration use the vault credential for a vault-served fallback - #199
Open
iceteaSA wants to merge 4 commits into
Open
fix: prewarm and profile hydration use the vault credential for a vault-served fallback#199iceteaSA wants to merge 4 commits into
iceteaSA wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 6 files
Architecture diagram
sequenceDiagram
participant Client as OpenCode Client
participant Plugin as Anthropic Auth Plugin
participant Resolver as Custody Resolver
participant Vault as Vault Credential Service
participant CacheKeep as CacheKeep Manager
participant Provider as Anthropic API
participant Reporter as Auth Failure Reporter
participant Profile as Profile and Quota Hydration
Note over Client,Provider: Runtime flow for OAuth fallback accounts with optional vault custody
Client->>Plugin: Send authenticated request
Plugin->>Resolver: Resolve fallback credential
alt Vault enabled and resident credential usable
Resolver->>Vault: Read resident credential by handle
Vault-->>Resolver: Vault token and record_version
Resolver-->>Plugin: Vault token with served provenance
else Vault enabled but credential cold
Resolver-->>Plugin: Credential unavailable
Plugin-->>Client: Skip dependent operation and retry later
else Vault disabled or blocked
Resolver-->>Plugin: Stored sidecar access token
end
Plugin->>Provider: Request with Authorization bearer token
Provider-->>Plugin: Response
Plugin-->>Client: Stream or request result
opt Quota polling
Plugin->>Resolver: Resolve fallback credential
Resolver-->>Plugin: Vault token or sidecar token
Plugin->>Provider: GET OAuth usage with resolved token
Provider-->>Plugin: Quota snapshot
end
opt Prime and recovery prewarm
Plugin->>Resolver: Resolve credential for prewarm
alt Vault credential resident
Resolver-->>Plugin: Vault token and served record_version
Plugin->>Provider: Prewarm messages request with vault token
Provider-->>Plugin: Prewarm response
else Vault credential cold
Resolver-->>Plugin: vault-cold
Plugin-->>Plugin: Skip request and log expected cold state
else Vault disabled
Resolver-->>Plugin: Refreshed sidecar token
Plugin->>Provider: Prewarm messages request with sidecar token
Provider-->>Plugin: Prewarm response
end
opt Vault-token prewarm returns 401
Plugin->>Reporter: Report auth failure
Reporter->>Vault: report_auth_failure
Vault-->>Reporter: Report accepted
Note over Plugin,Reporter: Reports are deduplicated by handle and record_version. Sidecar 401s are not reported
end
end
opt CacheKeep hourly prewarm
CacheKeep->>Plugin: prepareHeaders target
Plugin->>Resolver: Resolve fallback credential
alt Vault credential resident
Resolver-->>Plugin: Vault token and served provenance
Plugin-->>CacheKeep: Headers with vault token
CacheKeep->>Provider: Cache prewarm with vault token
Provider-->>CacheKeep: Prewarm response
else Vault credential cold
Resolver-->>Plugin: No usable credential
Plugin-->>CacheKeep: transient unavailable result
CacheKeep-->>CacheKeep: Retain target and cache expiry. Bounded retry
else Vault disabled
Resolver-->>Plugin: Refreshed sidecar token
Plugin-->>CacheKeep: Headers with sidecar token
CacheKeep->>Provider: Cache prewarm with sidecar token
Provider-->>CacheKeep: Prewarm response
end
opt Vault-token prewarm returns 401
CacheKeep->>Reporter: Report captured handle and record_version
Reporter->>Vault: report_auth_failure
Vault-->>Reporter: Report accepted
end
end
opt Profile hydration
Plugin->>Resolver: Resolve fallback credential
alt Vault credential resident
Resolver-->>Plugin: Vault token
Plugin->>Profile: Fetch OAuth account profile with vault token
Profile->>Provider: GET OAuth profile
Provider-->>Profile: Organization and tier
Profile-->>Plugin: Hydrated profile
else Vault credential cold
Resolver-->>Plugin: No usable credential
Plugin-->>Plugin: Skip hydration and retry next tick
else Vault disabled
Resolver-->>Plugin: Stored sidecar token
Plugin->>Profile: Fetch OAuth account profile with sidecar token
Profile->>Provider: GET OAuth profile
Provider-->>Profile: Organization and tier
Profile-->>Plugin: Hydrated profile
end
end
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…lear it on every exit; count a cold-vault prime as a skip
There was a problem hiding this comment.
1 issue found across 10 files (changes from recent commits).
Confidence score: 4/5
packages/opencode/src/tests/index.test.tsonly checks thatreport_auth_failurewas not called, so it could pass without proving the first prewarm used the vault token and the second used the sidecar fallback; add positive assertions for both token sources to make the test catch regressions.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/opencode/src/tests/index.test.ts">
<violation number="1" location="packages/opencode/src/tests/index.test.ts:2177">
P3: This test only asserts zero report_auth_failure calls; it never verifies that the first prewarm actually served the vault token or that the second fell back to the sidecar token. Add a positive control so the test would fail if both prewarms used the sidecar credential.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| } | ||
| }) | ||
|
|
||
| test('CacheKeep clears a failed vault attempt before a sidecar 401', async () => { |
There was a problem hiding this comment.
P3: This test only asserts zero report_auth_failure calls; it never verifies that the first prewarm actually served the vault token or that the second fell back to the sidecar token. Add a positive control so the test would fail if both prewarms used the sidecar credential.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/tests/index.test.ts, line 2177:
<comment>This test only asserts zero report_auth_failure calls; it never verifies that the first prewarm actually served the vault token or that the second fell back to the sidecar token. Add a positive control so the test would fail if both prewarms used the sidecar credential.</comment>
<file context>
@@ -2174,6 +2174,199 @@ describe('fallback Claustrum credential resolution', () => {
}
})
+ test('CacheKeep clears a failed vault attempt before a sidecar 401', async () => {
+ const calls: CredentialCall[] = []
+ const storage = fallbackWithClaustrum({
</file context>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #197 (prime). Same defect class, two more sites.
Bug
A vault-served fallback's
account.accessis the frozen sidecar token; the vault's first rotation revokes it. Two paths still sent it:prepareHeaders, fallback branch):refreshAccountis a deliberate no-op for a vault-served account, thenaccessToken = current.access. Every 1h-cache keepalive on the custodied fallback 401'd silently, so its cache entries were never extended. The Fable/Opus recovery source-model prewarm and the Claude Code bootstrap call ride the same provider and inherited the wrong token.const accessToken = account.access→fetchOAuthAccountProfile. Tier never refreshed for a vault-served fallback; the 401 was swallowed.The quota poll (v1.22.0) and prime (#197) were the first two instances. A census of every outbound use of a fallback token found these as the remaining two.
Fix
Both sites resolve through the same custody resolver prime uses:
{ ok: false, transient: true }: target retained,cacheExpiresAtuntouched, bounded retry — the fix(core): contain thrown prewarm errors in the cachekeep tick #155 fetch-timeout shape); profile hydration skips and retries next tick;A 401 on a vault-token prewarm reports
report_auth_failureonce with the record_version the prewarm was sent with (captured at send; a re-peek after a concurrent rotation would name a superseded version), through the same(handle, version)dedup as prime. A sidecar-token 401 never reports.Census test
fallback-token-use-sites.test.ts: oneitper outbound site — request path (+ lane start + request bootstrap), quota poll, prime, CacheKeep prewarm (+ prewarm bootstrap), recovery source-model prewarm, profile hydration. Each configures a vault-served fallback with sidecarsk-ant-oat01-sidecar-canary-<site>and residentsk-ant-oat01-vault-<site>, drives the real path, and asserts every captured Authorization is the vault token and the canary appears in no request, with a positive control on request count. ReintroducingaccessToken = current.accessinprepareHeadersreds exactly the two sites that share it (cachekeep, recovery); the other four stay green. A fifth instance of this class now names its site.Verification
Production mutations red first: cachekeep
current.access; profileaccount.access; cold vault falling through to sidecar; provenance fence dropped (0 reports); re-peek at report time after a rotation (report lost). Reviewer (MiniMax M3) re-ran all five independently: APPROVE 0/0.Gates: core build, root
bun run test169/0 core + 1566/0 opencode, typecheck, format:check, biome.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Fixes CacheKeep prewarm and profile hydration sending the frozen sidecar token for vault-served fallbacks, so the vault-issued credential is used and 401s are reported correctly.
Behavior
skippedinstead of a fire-failed error.Written for commit bd6abcc. Summary will update on new commits.