Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c99b6f5
feat(feed): publish the Anthropic account uuid and the lease horizon …
iceteaSA Sep 3, 2026
64e0fa9
fix(feed): reap stale sibling leases and document the per-process uni…
iceteaSA Sep 3, 2026
82105d3
fix(feed): fence the account uuid on credential lineage and make leas…
iceteaSA Sep 4, 2026
7ebf6e7
feat(custody): per-account custody toggle and the global-mode custody…
iceteaSA Sep 7, 2026
030a315
feat(custody): Claustrum handle-manifest layer with the cross-tenant …
iceteaSA Sep 7, 2026
dd9076f
feat(custody): global claustrum mode with one locked writer; retire t…
iceteaSA Sep 7, 2026
fb627d5
feat(custody): tombstone write/recognise/refuse split, login refusal …
iceteaSA Sep 7, 2026
9b9a5e6
feat(custody): vault-only routing under claustrum with the cold-route…
iceteaSA Sep 7, 2026
9b9892d
feat(custody): main vault 401 reports are fenced to the send-time rec…
iceteaSA Sep 7, 2026
2ffa8aa
feat(identity): bind main quota identity to the vault credential; nev…
iceteaSA Sep 7, 2026
81bf1bd
feat(custody): the local exit clears a binding only after a verified …
iceteaSA Sep 7, 2026
db4e9e1
feat(custody): takeover coordinator, live adapters, two-phase boot ba…
iceteaSA Sep 7, 2026
4b1dbec
feat(custody): takeover writes with a mode-last commit, staged rollba…
iceteaSA Sep 7, 2026
682c2a6
fix(custody): review hardening — rollback order, tombstone bearer, mo…
iceteaSA Sep 7, 2026
4004a61
test(custody): whole-reach host-write invariant, route-local fallback…
iceteaSA Sep 7, 2026
581af55
fix(custody): fence in-flight refreshes and stale credential writes a…
iceteaSA Sep 7, 2026
44fbd35
refactor(custody): extract custody dimensions and takeover orchestrat…
iceteaSA Sep 7, 2026
6b5d9fc
Merge feat/feed-account-uuid (PR #198) into feat/custody-toggle
iceteaSA Sep 7, 2026
dbb64f7
test(custody): hermetic first-run acceptance suite; a rostered accoun…
iceteaSA Sep 7, 2026
0916d99
fix(custody): a throwing post-commit verifier reverts the mode; trans…
iceteaSA Sep 7, 2026
dd00d66
test(e2e): custody mode through a real OpenCode process against a fak…
iceteaSA Sep 7, 2026
177d71c
fix(custody): quota policy on empty vault rows, serialized local exit…
iceteaSA Sep 7, 2026
d1e58ad
docs: global Claustrum custody for operators; define the matrix axes
iceteaSA Sep 7, 2026
5d42c6c
fix(custody): e2e daemon strictness, quota pass under verification, s…
iceteaSA Sep 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

This repo is a CortexKit-maintained Anthropic auth monorepo for OpenCode and Pi. The OpenCode package is a fork of the original `@ex-machina/opencode-anthropic-auth` plugin, so older entries below the initial CortexKit release are inherited from upstream package history.

## Unreleased

### Minor Changes

- Document the global `/claude-account claustrum|local` custody mode, including its fail-closed OpenCode behavior and Pi refusal.
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
- Document global Claustrum manifest onboarding, startup legacy-handle migration, and vault re-login recovery.
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.

## 1.22.0

### Minor Changes
Expand Down
46 changes: 23 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,21 @@ Override the path with `PI_ANTHROPIC_AUTH_FILE`. The package also respects `PI_A

## Primary account authentication

Each integration keeps the host agent's normal Anthropic login as the primary account.

For OpenCode, use OpenCode's Anthropic auth flow:
OpenCode keeps its native `anthropic` entry as the main account. In `local` mode, sign in through OpenCode:

```text
/connect anthropic
```

The primary account remains OpenCode's built-in `anthropic` auth entry. The OpenCode plugin intercepts final Anthropic requests and supplies the OAuth headers and request transforms needed for Claude Pro/Max access.
Before the main account can enter Claustrum custody, the operator onboards it into the vault with Claustrum's tooling. Until the dedicated import tooling lands, use the interim path in the custody state machine's §8, “Adding a new account today.” The plugin never runs `ck`, never imports or migrates credentials, and never writes the host auth slot.

Verify the migration gate against the installed `ck-auth` binary, not an announcement or a claimed deployment revision. In an isolated scratch data directory, a production-shaped tombstone import must refuse with `refusing Claustrum tombstone material` and leave the audit chain unchanged; in the same run, a real-material import with `--replace` must succeed. The refusal alone is not enough because a broken import path also refuses. The exact tombstone write is:

```json
{ "type": "oauth", "access": "", "refresh": "claustrum-tombstone:v1:anthropic", "expires": 0 }
```

`access` is empty so the sealer's shape gate rejects the material by construction.

OpenCode's upstream authentication options are still supported:

Expand Down Expand Up @@ -186,9 +192,6 @@ Example:
"quotaHeaderFeed": {
"enabled": false
},
"claustrum": {
"accounts": {}
},
"killswitch": {
"enabled": false,
"main": { "five_hour": 5, "seven_day": 10, "scoped": 0 },
Expand Down Expand Up @@ -233,8 +236,6 @@ The `routing` block controls `/claude-routing`, `claudeCache` controls `/claude-

`quotaHeaderFeed.enabled` is an OpenCode-only, restart-required opt-in. It publishes only allowlisted quota-window values, an opaque account reference, an observation timestamp, and the configured OAuth-account count; it never publishes tokens, raw headers, request bodies, model IDs, or refresh errors. Per-process lease files use owner-only permissions under `$TMPDIR/opencode-anthropic-auth/quota-header-feed`, expire after three minutes, and can be redirected with `OPENCODE_ANTHROPIC_AUTH_QUOTA_FEED_DIR`.

`claustrum.accounts.<fallback-account-id>.enabled` is an OpenCode-only, per-account opt-in for fallback credential custody. It is inert unless that account's capability handle has been provisioned in `anthropic-auth-state.json`; handles are bearer credentials and must not be copied into the public config file.
Comment thread
iceteaSA marked this conversation as resolved.

Runtime data is stored separately in `anthropic-auth-state.json`: fallback OAuth tokens, API-route keys, token refresh backoff, quota snapshots, and quota API backoff. `sticky-balanced` session assignments use a separate `anthropic-auth-routing-state.json`; session IDs are SHA-256 hashed in that file. Background refresh and quota checks write only runtime state, so editing `anthropic-auth.json` does not get overwritten by another running plugin instance.

## OpenCode lane-start setting
Expand Down Expand Up @@ -287,24 +288,15 @@ Fallback OAuth tokens refresh in the background so idle accounts do not expire b

If Anthropic reports `invalid_grant`, that account must be logged in again. `/claude-account reset-backoff` manually clears the main account's refresh backoff and its matching quota backoff.

### Optional Claustrum custody (OpenCode)

OpenCode can obtain an opted-in fallback OAuth account's access credential from a local [Claustrum](https://github.com/cortexkit/claustrum) daemon instead of refreshing that account independently. After provisioning the account's runtime handle, enable custody by account ID:
### Claustrum manifest service (OpenCode)

```json
{
"claustrum": {
"accounts": {
"personal-alt": { "enabled": true }
}
}
}
```
Claustrum custody is global. `/claude-account claustrum` enters custody, `/claude-account local` returns to local authentication, and bare `/claude-account` shows status. There are no per-account custody switches. Claustrum uses a handle manifest written by Claustrum tooling.

The request path reads only a resident in-memory credential. Startup warming and periodic custody ticks perform vault I/O and keep idle credentials refreshed; a cold or unavailable vault falls back to the sidecar credential path. Vault-served 401 reports carry the exact record version and response provenance, including relay-stream 401s, so a sidecar-served failure cannot invalidate a healthy vault credential. `/claude-account` and the OpenCode account modal show the gate, current vault service, and vault reauthentication state without exposing capability handles.
Entering custody preflights every enabled OAuth account. A refusal changes nothing, and the command reports every refusal in account order. The main account must already have been migrated by the operator. The plugin does not create or import vault records during this check.

Custody currently applies only to fallback OAuth accounts. Main-account vault service is not implemented. If Claustrum has replaced the main host credential with its provider-bound tombstone, the plugin rejects refresh locally without contacting Anthropic or persisting a permanent `invalid_grant` state.
In custody, every enabled OAuth route is served from the vault, including the main account. A cold main vault record returns a typed startup refusal and holds every OAuth route until the next viable boot; after a warm boot, it returns a typed provider-unavailable error. The plugin does not fall back to sidecar credentials or send a tombstone as a bearer token. A cold fallback is excluded only for that request, so other warm routes can still serve.

Leaving custody puts the main account back into interactive OpenCode sign-in. A fallback binding clears only after a login completed through the plugin's own login flow observes new credential material. To enter custody again for that fallback, the operator must import the new material into the vault with `--replace`; until then, `/claude-account claustrum` refuses with `binding_missing`. API-key routes are unaffected.
## Quota-aware routing

When `quota.enabled` is true, the plugin checks Anthropic's OAuth usage endpoint and applies the configured remaining-quota thresholds to both main and fallback accounts.
Expand Down Expand Up @@ -332,6 +324,14 @@ In OpenCode, this includes the main Anthropic account and sidecar fallback accou

Reset times are rendered as relative durations, such as `resets in 10m` or `resets in 1h 15m`.

### Quota header feed

The optional quota header feed writes one lease file per process under `${TMPDIR:-/tmp}/opencode-anthropic-auth/quota-header-feed/`. Set `OPENCODE_ANTHROPIC_AUTH_QUOTA_FEED_DIR` to override the directory. A file contains only accounts whose response headers THAT process harvested. Consumers MUST union entries from every file inside `lease_horizon_ms`, then deduplicate by account. "Newest file wins" drops accounts seen by other processes.

For each account, the newest entry wins for quota values. Resolve `anthropic_account_uuid` from any entry in that account's group that carries it. During a rollout, an older pre-restart publisher can write the newest entry without that field beside newer code that has it.

Each entry always includes `anthropic_account_uuid`; an absent key identifies an older producer. Its value is provider-derived or `null`, never a local substitute. A consumer that sees `null` must count a gap, not fall back to `account_ref`; that field is store-local and never a join key.

## Safety fallback (OpenCode)

Eligible Fable 5/5.1 and Opus 5 OAuth requests try Anthropic's server-side safety fallback first. The plugin sends `fallbacks: "default"` with Anthropic's server-side fallback beta, preserves fallback conversation boundaries in OpenCode history, and reports model handoffs and restoration in the TUI sidebar or OpenCode Desktop. Follow-up requests may remain on Anthropic's selected fallback model for approximately one hour.
Expand Down
Loading