instar 1.3.534 → 1.3.536
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/CredentialSwapExecutor.d.ts +234 -0
- package/dist/core/CredentialSwapExecutor.d.ts.map +1 -0
- package/dist/core/CredentialSwapExecutor.js +596 -0
- package/dist/core/CredentialSwapExecutor.js.map +1 -0
- package/dist/core/CredentialWriteFunnel.d.ts +16 -0
- package/dist/core/CredentialWriteFunnel.d.ts.map +1 -1
- package/dist/core/CredentialWriteFunnel.js +19 -0
- package/dist/core/CredentialWriteFunnel.js.map +1 -1
- package/dist/core/OAuthRefresher.d.ts +18 -1
- package/dist/core/OAuthRefresher.d.ts.map +1 -1
- package/dist/core/OAuthRefresher.js +23 -1
- package/dist/core/OAuthRefresher.js.map +1 -1
- package/dist/core/QuotaPoller.d.ts.map +1 -1
- package/dist/core/QuotaPoller.js +7 -0
- package/dist/core/QuotaPoller.js.map +1 -1
- package/dist/monitoring/AccountSwitcher.d.ts.map +1 -1
- package/dist/monitoring/AccountSwitcher.js +13 -2
- package/dist/monitoring/AccountSwitcher.js.map +1 -1
- package/dist/monitoring/CredentialProvider.d.ts +20 -0
- package/dist/monitoring/CredentialProvider.d.ts.map +1 -1
- package/dist/monitoring/CredentialProvider.js +26 -0
- package/dist/monitoring/CredentialProvider.js.map +1 -1
- package/package.json +4 -2
- package/scripts/lint-no-direct-destructive.js +4 -0
- package/scripts/lint-no-unfunneled-credential-write.js +162 -0
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/{1.3.534.md → 1.3.535.md} +22 -0
- package/upgrades/1.3.536.md +34 -0
- package/upgrades/side-effects/live-credential-repointing-increment-a-funnel-routing.md +86 -0
- package/upgrades/side-effects/ws52-step5-swap-executor.md +64 -0
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CredentialSwapExecutor — Step 5 of live credential re-pointing (spec §2.3).
|
|
3
|
+
*
|
|
4
|
+
* The staged exchange that MOVES a credential between two config-home slots WITHOUT restarting
|
|
5
|
+
* the sessions reading those slots (E3: the `claude` client re-reads its store on the next API
|
|
6
|
+
* call). `swap(slotA, slotB)` EXCHANGES the two slots' credentials — never copies — so the §0.d
|
|
7
|
+
* invariant ("one lineage per readable config home") holds by construction for the swap itself.
|
|
8
|
+
*
|
|
9
|
+
* Ships DARK + dry-run for EVERYONE (incl. dev). `subscriptionPool.credentialRepointing` is a
|
|
10
|
+
* `DARK_GATE_EXCLUSIONS` `destructive` entry (`enabled:false` + `dryRun:true`); with the feature
|
|
11
|
+
* off the executor performs ZERO real keychain/config writes (a strict no-op verdict), and with
|
|
12
|
+
* `dryRun` on it runs the full decision loop and AUDITS what it WOULD do without writing. Going
|
|
13
|
+
* live needs a deliberate `enabled:true` AND `dryRun:false` flip (the two-flag gate).
|
|
14
|
+
*
|
|
15
|
+
* ── The staged-exchange mechanism (spec §2.3 steps, each audited) ──
|
|
16
|
+
* 1. Preconditions: BOTH slots resolve by EXACT membership in the ledger's enumerated slot set
|
|
17
|
+
* BEFORE any path expansion (a value not `===` a known slot is rejected — `../`/`~`/absolute
|
|
18
|
+
* traversal can never reach a keychain service, since the value is validated against the
|
|
19
|
+
* ledger, not the filesystem); neither tenant quarantined; no swap in flight (single-mover);
|
|
20
|
+
* both blobs re-read fresh, parse, and carry refresh tokens.
|
|
21
|
+
* 1a. Source-slot CAS: immediately before staging/overwriting each slot, RE-READ its on-disk
|
|
22
|
+
* blob and compare to the step-1 read. If it changed and the new blob parses + identity-
|
|
23
|
+
* matches the SAME tenant → ADOPT the newer blob (the client's rotated copy); never carry a
|
|
24
|
+
* blob older than what is on disk. A different-tenant blob is a clobber-race → ABORT.
|
|
25
|
+
* 2. Staging escrow: COPY (never move) blob A (the 1a re-read, freshest) into a DISJOINT
|
|
26
|
+
* `instar-credential-swap-staging-<swapId>` keychain entry; journal `begin` BEFORE the first
|
|
27
|
+
* destructive write. The staging namespace is guaranteed disjoint from every
|
|
28
|
+
* `claudeCredentialService(home)` output, so no `claude` client / poller ever reads a staged
|
|
29
|
+
* copy. Staging is RETAINED until step 6's delayed re-verify passes.
|
|
30
|
+
* 3. The exchange: write blob B → slot A, write blob A (from staging) → slot B — KEYCHAIN FIRST,
|
|
31
|
+
* then config (`oauthAccount` blocks; default slot config = `~/.claude.json`, home-root).
|
|
32
|
+
* 4. Verify on ACCOUNT IDENTITY via the oracle (NOT token bytes, NOT auth-status). match→commit;
|
|
33
|
+
* identity-MISMATCH→repair-from-staging then re-verify, still-wrong→quarantine;
|
|
34
|
+
* oracle-UNAVAILABLE→QUARANTINE-NEVER-REPAIR (an outage must never trigger a destructive
|
|
35
|
+
* repair). The system NEVER writes a credential into a slot it cannot identity-verify.
|
|
36
|
+
* 5. Commit: journal `committed`, ledger assignments updated — staging RETAINED (a client
|
|
37
|
+
* write-back between commit and step-6 could clobber slot B's only copy; staging is the heal
|
|
38
|
+
* source for that window).
|
|
39
|
+
* 6. Delayed re-verify ~90s after commit (oracle identity of both slots); only on pass →
|
|
40
|
+
* DELETE staging + journal `done`. (90s covers the sub-2-min in-flight refresh; the at-expiry
|
|
41
|
+
* write-back is the always-on §2.4 audit's job.)
|
|
42
|
+
*
|
|
43
|
+
* ── Crash-safety ── A crash at ANY boundary is decidable from the journal + the two slots' on-disk
|
|
44
|
+
* reads: before step 3's first write nothing destructive happened (staging is a COPY, slot A
|
|
45
|
+
* untouched) → unwind is a no-op; after `begin` and before `done`, staging is the escrow and
|
|
46
|
+
* recovery applies the adopt-on-newer rule (never a blind staging overwrite). `recover()` acquires
|
|
47
|
+
* the single-mover mutex (recovery WRITES race the boot balancer otherwise) and resolves every
|
|
48
|
+
* in-flight journal row; the balancer's first pass is gated on `recoveryBarrier` WITH a bounded
|
|
49
|
+
* hang-timeout (a wedged recovery write must not freeze the balancer forever — on timeout the
|
|
50
|
+
* unresolved slots are quarantined BEFORE the barrier lifts, so the balancer structurally cannot
|
|
51
|
+
* select them).
|
|
52
|
+
*
|
|
53
|
+
* ── Concurrency ── Every swap runs inside `funnel.withSingleMover(() => funnel.withSlotLocks([A,B],
|
|
54
|
+
* …))` so two swaps never overlap and a swap can never interleave with a refresh on the same slot
|
|
55
|
+
* (the Step-4b funnel guards both). All `security` keychain calls go through the async
|
|
56
|
+
* execFile-based `KeychainCredentialExec` with a 10s timeout — NEVER `execFileSync` (a locked
|
|
57
|
+
* keychain ACL prompt would wedge the event loop). The credential WRITE path routes through the
|
|
58
|
+
* funnel; the executor never calls `defaultCredentialStore.write` / `provider.writeCredentials`,
|
|
59
|
+
* so the §2.2 unfunneled-write lint stays clean.
|
|
60
|
+
*
|
|
61
|
+
* ── No token material on any surface ── audit records, journal details, attention items, and the
|
|
62
|
+
* swap-result reference accounts by id / slot only; every free-text error string is scrubbed
|
|
63
|
+
* through `redactToken` before it reaches any persisted/served/notified surface.
|
|
64
|
+
*/
|
|
65
|
+
import { execFile } from 'node:child_process';
|
|
66
|
+
import { credentialSlotKey, claudeCredentialService, expandHome } from './OAuthRefresher.js';
|
|
67
|
+
import { redactToken } from '../monitoring/CredentialProvider.js';
|
|
68
|
+
const SECURITY_CALL_TIMEOUT_MS = 10_000;
|
|
69
|
+
/** Disjoint from every `claudeCredentialService(home)` output (`Claude Code-credentials[-<8hex>]`). */
|
|
70
|
+
const STAGING_SERVICE_PREFIX = 'instar-credential-swap-staging-';
|
|
71
|
+
const DEFAULT_REVERIFY_DELAY_MS = 90_000;
|
|
72
|
+
/** Hang-timeout barrier: a wedged recovery write must not freeze the balancer forever. */
|
|
73
|
+
const DEFAULT_RECOVERY_BARRIER_TIMEOUT_MS = 60_000;
|
|
74
|
+
/** Default macOS keychain exec — async `execFile`, 10s timeout. Non-darwin: a per-service file. */
|
|
75
|
+
export const defaultKeychainExec = {
|
|
76
|
+
readService(service) {
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
try {
|
|
79
|
+
execFile('security', ['find-generic-password', '-s', service, '-w'], { timeout: SECURITY_CALL_TIMEOUT_MS, maxBuffer: 1024 * 1024 }, (err, stdout) => {
|
|
80
|
+
if (err) {
|
|
81
|
+
resolve(null); // @silent-fallback-ok: no keychain entry (or timeout) → treated as absent; the caller's CAS/precondition decides, never a guess
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const raw = (stdout ?? '').toString().trim();
|
|
85
|
+
resolve(raw || null);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
resolve(null); // @silent-fallback-ok: spawn failed → absent; never throws into the swap loop
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
writeService(service, rawJson) {
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
try {
|
|
96
|
+
execFile('security', ['add-generic-password', '-U', '-a', process.env.USER ?? 'instar', '-s', service, '-w', rawJson], { timeout: SECURITY_CALL_TIMEOUT_MS }, (err) => (err ? reject(err) : resolve()));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
reject(err);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
deleteService(service) {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
try {
|
|
106
|
+
execFile('security', ['delete-generic-password', '-s', service], { timeout: SECURITY_CALL_TIMEOUT_MS }, () => resolve());
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
resolve(); // @silent-fallback-ok: spawn failed on a best-effort delete → leave the (harmless, disjoint) staging entry for the boot-recovery orphan sweep
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
/** No-op config exchange (the metadata follow is wired in Step 6's census re-routing). */
|
|
115
|
+
export const noopConfigBlockExchange = {
|
|
116
|
+
async exchange() {
|
|
117
|
+
/* metadata-follow wired in Step 6 (census #1/#8); the keychain exchange is the record of truth */
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
export class CredentialSwapExecutor {
|
|
121
|
+
funnel;
|
|
122
|
+
ledger;
|
|
123
|
+
keychain;
|
|
124
|
+
configExchange;
|
|
125
|
+
resolveIdentity;
|
|
126
|
+
config;
|
|
127
|
+
emitAudit;
|
|
128
|
+
emitAttention;
|
|
129
|
+
now;
|
|
130
|
+
nowIso;
|
|
131
|
+
reverifyDelayMs;
|
|
132
|
+
recoveryBarrierTimeoutMs;
|
|
133
|
+
swapIdFactory;
|
|
134
|
+
/** Resolves once boot-recovery has resolved every in-flight journal row (or the barrier timed out). */
|
|
135
|
+
recoveryResolve = null;
|
|
136
|
+
recoveryBarrier;
|
|
137
|
+
recoveryDone = false;
|
|
138
|
+
seq = 0;
|
|
139
|
+
constructor(deps) {
|
|
140
|
+
this.funnel = deps.funnel;
|
|
141
|
+
this.ledger = deps.ledger;
|
|
142
|
+
this.keychain = deps.keychain ?? defaultKeychainExec;
|
|
143
|
+
this.configExchange = deps.configExchange ?? noopConfigBlockExchange;
|
|
144
|
+
this.resolveIdentity = deps.resolveIdentity;
|
|
145
|
+
this.config = { enabled: deps.config?.enabled ?? false, dryRun: deps.config?.dryRun ?? true };
|
|
146
|
+
this.emitAudit = deps.emitAudit;
|
|
147
|
+
this.emitAttention = deps.emitAttention;
|
|
148
|
+
this.now = deps.now ?? (() => Date.now());
|
|
149
|
+
this.nowIso = deps.nowIso ?? (() => new Date(this.now()).toISOString());
|
|
150
|
+
this.reverifyDelayMs = deps.reverifyDelayMs ?? DEFAULT_REVERIFY_DELAY_MS;
|
|
151
|
+
this.recoveryBarrierTimeoutMs = deps.recoveryBarrierTimeoutMs ?? DEFAULT_RECOVERY_BARRIER_TIMEOUT_MS;
|
|
152
|
+
this.swapIdFactory = deps.swapIdFactory ?? (() => `${Date.now().toString(36)}-${(this.seq++).toString(36)}`);
|
|
153
|
+
this.recoveryBarrier = new Promise((resolve) => {
|
|
154
|
+
this.recoveryResolve = resolve;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The balancer's first pass awaits THIS before selecting any pair — so a recovery WRITE can't
|
|
159
|
+
* race a fresh swap on a different slot pair (round-2 unmutexed write-write find). Bounded by a
|
|
160
|
+
* hang-timeout: a wedged recovery write must not freeze the balancer forever.
|
|
161
|
+
*/
|
|
162
|
+
awaitRecoveryComplete() {
|
|
163
|
+
return this.recoveryBarrier;
|
|
164
|
+
}
|
|
165
|
+
isRecoveryComplete() {
|
|
166
|
+
return this.recoveryDone;
|
|
167
|
+
}
|
|
168
|
+
// ── Audit / scrub chokepoint ────────────────────────────────────────────────────────────────
|
|
169
|
+
/** SINGLE emit chokepoint — every record is scrubbed of token material here (§2.9). */
|
|
170
|
+
audit(swapId, step, slotA, slotB, detail) {
|
|
171
|
+
if (!this.emitAudit)
|
|
172
|
+
return;
|
|
173
|
+
this.emitAudit({ swapId, step, slotA, slotB, detail: detail ? scrub(detail) : undefined, at: this.nowIso() });
|
|
174
|
+
}
|
|
175
|
+
stagingService(swapId) {
|
|
176
|
+
return `${STAGING_SERVICE_PREFIX}${swapId}`;
|
|
177
|
+
}
|
|
178
|
+
// ── The swap (spec §2.3) ──────────────────────────────────────────────────────────────────────
|
|
179
|
+
/**
|
|
180
|
+
* EXCHANGE the credentials of two slots. Returns a verdict; never throws into the caller for an
|
|
181
|
+
* expected condition (precondition fail / clobber-race / quarantine / busy lock are all results).
|
|
182
|
+
*/
|
|
183
|
+
async swap(slotA, slotB) {
|
|
184
|
+
// DARK: feature off → strict no-op (zero writes). This is the dark-ship inertness guarantee.
|
|
185
|
+
if (!this.config.enabled) {
|
|
186
|
+
return { outcome: 'disabled', reason: 'credential re-pointing is disabled (dark) — no swap performed' };
|
|
187
|
+
}
|
|
188
|
+
// Step 1 (precondition, pre-lock): EXACT ledger membership BEFORE any path expansion. A value
|
|
189
|
+
// not === a known slot is rejected — `../`/`~`/absolute traversal can never reach a keychain
|
|
190
|
+
// service because we validate against the ledger's enumerated set, not the filesystem.
|
|
191
|
+
const memberSlots = new Set(this.ledger.getAssignments().map((a) => a.slot));
|
|
192
|
+
if (slotA === slotB) {
|
|
193
|
+
return { outcome: 'precondition-failed', reason: 'slotA and slotB are the same slot' };
|
|
194
|
+
}
|
|
195
|
+
if (!memberSlots.has(slotA) || !memberSlots.has(slotB)) {
|
|
196
|
+
return {
|
|
197
|
+
outcome: 'precondition-failed',
|
|
198
|
+
reason: `a slot is not a known ledger member (got '${slotA}', '${slotB}') — refusing an arbitrary keychain/path write`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const aAssign = this.ledger.getAssignment(slotA);
|
|
202
|
+
const bAssign = this.ledger.getAssignment(slotB);
|
|
203
|
+
if (aAssign?.quarantined || bAssign?.quarantined) {
|
|
204
|
+
return { outcome: 'precondition-failed', reason: 'a slot tenant is quarantined — excluded from balancing' };
|
|
205
|
+
}
|
|
206
|
+
// Take the single-mover mutex (no swap in flight) THEN both slot locks (canonical order). The
|
|
207
|
+
// funnel returns a SKIP rather than blocking → a busy lock is a transient retry, never a wedge.
|
|
208
|
+
const moverResult = await this.funnel.withSingleMover(async () => {
|
|
209
|
+
return this.funnel.withSlotLocks([credentialSlotKey(slotA), credentialSlotKey(slotB)], () => this.swapLocked(slotA, slotB, aAssign?.accountId ?? '', bAssign?.accountId ?? ''));
|
|
210
|
+
});
|
|
211
|
+
if (!moverResult.ran) {
|
|
212
|
+
return { outcome: 'skipped', reason: scrub(moverResult.skippedReason ?? 'funnel busy') };
|
|
213
|
+
}
|
|
214
|
+
const inner = moverResult.value;
|
|
215
|
+
if (!inner.ran) {
|
|
216
|
+
return { outcome: 'skipped', reason: scrub(inner.skippedReason ?? 'slot lock busy') };
|
|
217
|
+
}
|
|
218
|
+
return inner.value;
|
|
219
|
+
}
|
|
220
|
+
/** Runs under the single-mover mutex + both slot locks. */
|
|
221
|
+
async swapLocked(slotA, slotB, expectA, expectB) {
|
|
222
|
+
const swapId = this.swapIdFactory();
|
|
223
|
+
// Step 1 (continued): re-read both blobs fresh; they must parse + carry refresh tokens.
|
|
224
|
+
const read1A = await this.readBlob(slotA);
|
|
225
|
+
const read1B = await this.readBlob(slotB);
|
|
226
|
+
if (!read1A || !read1B) {
|
|
227
|
+
return { outcome: 'precondition-failed', reason: `a slot blob is missing/unparseable or lacks a refresh token`, swapId };
|
|
228
|
+
}
|
|
229
|
+
// Step 1a: source-slot CAS — re-read each slot immediately before it is staged/overwritten. If
|
|
230
|
+
// a slot changed AND the new blob is the SAME tenant → ADOPT it (the client's rotated copy). A
|
|
231
|
+
// DIFFERENT-tenant blob is a clobber-race → ABORT, overwrite nothing.
|
|
232
|
+
const casA = await this.casReread(slotA, read1A, expectA, swapId);
|
|
233
|
+
if (casA.aborted)
|
|
234
|
+
return { outcome: 'clobber-race', reason: casA.reason, swapId };
|
|
235
|
+
const casB = await this.casReread(slotB, read1B, expectB, swapId);
|
|
236
|
+
if (casB.aborted)
|
|
237
|
+
return { outcome: 'clobber-race', reason: casB.reason, swapId };
|
|
238
|
+
const blobA = casA.blob; // freshest A (adopted if the client rotated it)
|
|
239
|
+
const blobB = casB.blob; // freshest B
|
|
240
|
+
// dryRun: full decision loop ran, ZERO writes. Audit the WOULD-swap and stop before step 2.
|
|
241
|
+
if (this.config.dryRun) {
|
|
242
|
+
this.audit(swapId, 'dry-run', slotA, slotB, `would exchange ${expectA}<->${expectB}`);
|
|
243
|
+
return { outcome: 'dry-run', reason: 'dry-run — decision computed, zero credential writes', swapId };
|
|
244
|
+
}
|
|
245
|
+
// Step 2: staging escrow — COPY blob A into a disjoint staging entry, THEN journal `begin`
|
|
246
|
+
// BEFORE the first destructive write. (COPY not move: slot A is untouched until step 3, so a
|
|
247
|
+
// crash before step 3 unwinds to a true no-op.)
|
|
248
|
+
const staging = this.stagingService(swapId);
|
|
249
|
+
await this.keychain.writeService(staging, blobA.raw);
|
|
250
|
+
this.ledger.appendJournal({ op: 'swap', phase: 'begin', slots: [slotA, slotB], detail: `swapId=${swapId} staging` });
|
|
251
|
+
this.audit(swapId, 'begin', slotA, slotB, `staged A; exchanging ${expectA}<->${expectB}`);
|
|
252
|
+
// Step 3: the exchange — KEYCHAIN FIRST (write B→slotA, then A→slotB from staging), config
|
|
253
|
+
// second. The between-the-two-writes crash point is recovery-decidable (journal begin + the
|
|
254
|
+
// two slots' on-disk reads + the retained staging copy of A).
|
|
255
|
+
await this.keychain.writeService(claudeCredentialService(slotA), blobB.raw);
|
|
256
|
+
await this.keychain.writeService(claudeCredentialService(slotB), blobA.raw);
|
|
257
|
+
this.ledger.appendJournal({ op: 'swap', phase: 'exchanged', slots: [slotA, slotB], detail: `swapId=${swapId} keychain` });
|
|
258
|
+
try {
|
|
259
|
+
await this.configExchange.exchange(slotA, slotB);
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
// Config-write failure AFTER a successful keychain exchange is a REPAIRABLE metadata
|
|
263
|
+
// condition — never a quarantine. Audited; the metadata-follow retry is the §2.3 step-3 path.
|
|
264
|
+
this.audit(swapId, 'config-exchange-deferred', slotA, slotB, `metadata follow failed (repairable): ${err?.message ?? 'unknown'}`);
|
|
265
|
+
}
|
|
266
|
+
// Step 4: verify on ACCOUNT IDENTITY. The credential now in slotA must be expectB's; the one
|
|
267
|
+
// in slotB must be expectA's.
|
|
268
|
+
const verify = await this.verifyAndHeal(slotA, slotB, expectB, expectA, staging, blobB, blobA, swapId);
|
|
269
|
+
if (verify.quarantined) {
|
|
270
|
+
return { outcome: 'quarantined', reason: verify.reason, swapId, quarantinedSlots: verify.quarantinedSlots };
|
|
271
|
+
}
|
|
272
|
+
// Step 5: commit — ledger assignments updated; staging RETAINED (heal source through step 6).
|
|
273
|
+
this.ledger.appendJournal({ op: 'swap', phase: 'verified', slots: [slotA, slotB], detail: `swapId=${swapId}` });
|
|
274
|
+
this.ledger.recordAssignment(slotA, expectB, { verifiedAt: this.nowIso(), op: 'swap' });
|
|
275
|
+
this.ledger.recordAssignment(slotB, expectA, { verifiedAt: this.nowIso(), op: 'swap' });
|
|
276
|
+
this.audit(swapId, 'committed', slotA, slotB, `tenants exchanged (staging retained)`);
|
|
277
|
+
// Step 6: delayed re-verify, then staging delete. Scheduled (does NOT block the swap return);
|
|
278
|
+
// a client whose refresh exchange was in flight DURING the swap lands its write after step 4.
|
|
279
|
+
this.scheduleReverify(slotA, slotB, expectB, expectA, staging, swapId);
|
|
280
|
+
return { outcome: 'swapped', reason: 'credentials exchanged; staging retained pending delayed re-verify', swapId };
|
|
281
|
+
}
|
|
282
|
+
// ── Step 1a: source-slot CAS ──────────────────────────────────────────────────────────────────
|
|
283
|
+
async casReread(slot, read1, expectTenant, swapId) {
|
|
284
|
+
const fresh = await this.readBlob(slot);
|
|
285
|
+
if (!fresh) {
|
|
286
|
+
// The slot became unreadable between step 1 and the destructive write → abort (do not write a
|
|
287
|
+
// stale copy over an absent/garbage on-disk state).
|
|
288
|
+
return { aborted: true, reason: `slot '${slot}' became unreadable before write (CAS)` };
|
|
289
|
+
}
|
|
290
|
+
if (fresh.raw === read1.raw) {
|
|
291
|
+
return { aborted: false, blob: read1 }; // unchanged
|
|
292
|
+
}
|
|
293
|
+
// It changed. Is the new blob the SAME tenant? Adopt; else it's a clobber-race → abort.
|
|
294
|
+
const identity = await this.resolveIdentity(slot);
|
|
295
|
+
if ('unavailable' in identity) {
|
|
296
|
+
// Cannot confirm the tenant of the changed blob → do NOT guess. Abort the swap (the safe
|
|
297
|
+
// direction: never overwrite a blob we cannot identity-verify is the same lineage).
|
|
298
|
+
return { aborted: true, reason: `CAS: slot '${slot}' changed and identity is unavailable — aborting (${identity.reason})` };
|
|
299
|
+
}
|
|
300
|
+
if (identity.accountId !== expectTenant) {
|
|
301
|
+
this.audit(swapId, 'clobber-race', slot, slot, `CAS saw a different-tenant blob (expected ${expectTenant})`);
|
|
302
|
+
return { aborted: true, reason: `CAS: slot '${slot}' now holds a different tenant — clobber-race, aborting` };
|
|
303
|
+
}
|
|
304
|
+
// Same tenant, newer blob → ADOPT (the client's rotated copy).
|
|
305
|
+
this.audit(swapId, 'cas-adopt', slot, slot, `adopted the client's rotated same-tenant blob`);
|
|
306
|
+
return { aborted: false, blob: fresh };
|
|
307
|
+
}
|
|
308
|
+
// ── Step 4: verify-and-heal (identity match → adopt / mismatch → repair / unavailable → quarantine) ──
|
|
309
|
+
async verifyAndHeal(slotA, slotB, wantA, wantB, staging, blobForA, blobForB, swapId) {
|
|
310
|
+
const quarantined = [];
|
|
311
|
+
for (const [slot, want, blob] of [
|
|
312
|
+
[slotA, wantA, blobForA],
|
|
313
|
+
[slotB, wantB, blobForB],
|
|
314
|
+
]) {
|
|
315
|
+
const id = await this.resolveIdentity(slot);
|
|
316
|
+
if ('unavailable' in id) {
|
|
317
|
+
// Oracle UNAVAILABLE during verify → quarantine-never-repair (an outage must never trigger
|
|
318
|
+
// a destructive repair). Leave the slot quarantined; the scheduled §2.4 re-probe clears it.
|
|
319
|
+
this.ledger.quarantineSlot(slot, scrub(`oracle unavailable at verify: ${id.reason}`));
|
|
320
|
+
await this.raiseQuarantineAttention(slot, swapId, 'oracle unavailable at verify');
|
|
321
|
+
this.audit(swapId, 'quarantine-oracle-unavailable', slotA, slotB, `slot=${slot}`);
|
|
322
|
+
quarantined.push(slot);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (id.accountId === want) {
|
|
326
|
+
continue; // identity match → good
|
|
327
|
+
}
|
|
328
|
+
// Identity MISMATCH with a reachable oracle → ONE repair from staging/fresh blob, re-verify.
|
|
329
|
+
this.audit(swapId, 'repair-attempt', slotA, slotB, `slot=${slot} expected=${want} got=${id.accountId}`);
|
|
330
|
+
await this.keychain.writeService(claudeCredentialService(slot), blob.raw);
|
|
331
|
+
const reId = await this.resolveIdentity(slot);
|
|
332
|
+
if (!('unavailable' in reId) && reId.accountId === want) {
|
|
333
|
+
this.audit(swapId, 'repair-ok', slotA, slotB, `slot=${slot}`);
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
// Still wrong (or the oracle went unavailable on re-verify) → quarantine, leave the other
|
|
337
|
+
// slot consistent. The bounded blast radius is one account re-auth (§6).
|
|
338
|
+
this.ledger.quarantineSlot(slot, scrub(`identity unrepairable after one attempt (wanted ${want})`));
|
|
339
|
+
await this.raiseQuarantineAttention(slot, swapId, 'identity mismatch unrepairable');
|
|
340
|
+
this.audit(swapId, 'quarantine-mismatch', slotA, slotB, `slot=${slot}`);
|
|
341
|
+
quarantined.push(slot);
|
|
342
|
+
}
|
|
343
|
+
if (quarantined.length > 0) {
|
|
344
|
+
return { quarantined: true, reason: 'one or more slots could not be identity-confirmed — quarantined (never repaired blindly)', quarantinedSlots: quarantined };
|
|
345
|
+
}
|
|
346
|
+
return { quarantined: false, reason: 'both slots identity-confirmed' };
|
|
347
|
+
}
|
|
348
|
+
// ── Step 6: delayed re-verify + staging delete ────────────────────────────────────────────────
|
|
349
|
+
scheduleReverify(slotA, slotB, wantA, wantB, staging, swapId) {
|
|
350
|
+
const run = async () => {
|
|
351
|
+
try {
|
|
352
|
+
await this.reverifyNow(slotA, slotB, wantA, wantB, staging, swapId);
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
// @silent-fallback-ok — the delayed re-verify is best-effort cleanup; a failure here leaves
|
|
356
|
+
// the journal at `committed` and staging RETAINED, which boot-recovery resolves (the
|
|
357
|
+
// crash-safe state). It must never throw out of a timer and crash the process.
|
|
358
|
+
this.audit(swapId, 'reverify-error', slotA, slotB, scrub(err?.message ?? 'unknown'));
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
const t = setTimeout(() => void run(), this.reverifyDelayMs);
|
|
362
|
+
if (typeof t.unref === 'function')
|
|
363
|
+
t.unref();
|
|
364
|
+
}
|
|
365
|
+
/** Run the step-6 re-verify immediately (exposed for deterministic tests). */
|
|
366
|
+
async reverifyNow(slotA, slotB, wantA, wantB, staging, swapId) {
|
|
367
|
+
let allGood = true;
|
|
368
|
+
for (const [slot, want] of [
|
|
369
|
+
[slotA, wantA],
|
|
370
|
+
[slotB, wantB],
|
|
371
|
+
]) {
|
|
372
|
+
const id = await this.resolveIdentity(slot);
|
|
373
|
+
if ('unavailable' in id || id.accountId !== want) {
|
|
374
|
+
// A write-back clobbered the slot (or the oracle is down). Do NOT blind-overwrite with
|
|
375
|
+
// staging (adopt-on-newer): re-read the slot; if it already identity-matches, the client
|
|
376
|
+
// healed it. Otherwise the honest outcome is needs-reauth/quarantine — surfaced, not papered.
|
|
377
|
+
allGood = false;
|
|
378
|
+
this.audit(swapId, 'reverify-divergence', slotA, slotB, `slot=${slot} want=${want}`);
|
|
379
|
+
if ('unavailable' in id) {
|
|
380
|
+
this.ledger.quarantineSlot(slot, scrub(`step-6 re-verify: oracle unavailable (${id.reason})`));
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
this.ledger.quarantineSlot(slot, scrub(`step-6 re-verify: slot diverged from ${want} (a client write-back)`));
|
|
384
|
+
}
|
|
385
|
+
await this.raiseQuarantineAttention(slot, swapId, 'delayed re-verify divergence');
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (allGood) {
|
|
389
|
+
// Both slots still correct → delete staging, journal `done`. Staging is the heal source ONLY
|
|
390
|
+
// through this point (retaining it past `done` would orphan it; the sweep predicate protects
|
|
391
|
+
// any non-`done` row).
|
|
392
|
+
await this.keychain.deleteService(staging);
|
|
393
|
+
this.ledger.appendJournal({ op: 'swap', phase: 'done', slots: [slotA, slotB], detail: `swapId=${swapId} reverified` });
|
|
394
|
+
this.audit(swapId, 'done', slotA, slotB, 'delayed re-verify passed; staging deleted');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// ── Boot recovery (single-mover mutex; resolves in-flight journal rows; barrier with hang-timeout) ──
|
|
398
|
+
/**
|
|
399
|
+
* Resolve every in-flight swap journal row left by a crash. Acquires the single-mover mutex (a
|
|
400
|
+
* recovery WRITE must not race a boot balancer pass on the ledger). When it finishes (or the
|
|
401
|
+
* hang-timeout fires) the recovery barrier lifts so the balancer's first pass can run. A wedged
|
|
402
|
+
* recovery write quarantines its slots BEFORE the barrier lifts, so the post-lift balancer
|
|
403
|
+
* structurally cannot select a wedged slot.
|
|
404
|
+
*/
|
|
405
|
+
async recover() {
|
|
406
|
+
if (this.recoveryDone)
|
|
407
|
+
return;
|
|
408
|
+
let lifted = false;
|
|
409
|
+
const lift = () => {
|
|
410
|
+
if (lifted)
|
|
411
|
+
return;
|
|
412
|
+
lifted = true;
|
|
413
|
+
this.recoveryDone = true;
|
|
414
|
+
this.recoveryResolve?.();
|
|
415
|
+
};
|
|
416
|
+
// Hang-timeout: a wedged recovery write (a keychain ACL prompt on the default slot) must not
|
|
417
|
+
// freeze the barrier forever. On timeout, quarantine the unresolved in-flight slots FIRST,
|
|
418
|
+
// THEN lift — the per-write 10s execFile timeout is what actually releases a held lock.
|
|
419
|
+
const inFlight = this.inFlightSwaps();
|
|
420
|
+
const barrierTimer = setTimeout(() => {
|
|
421
|
+
for (const e of inFlight) {
|
|
422
|
+
for (const slot of e.slots) {
|
|
423
|
+
try {
|
|
424
|
+
this.ledger.quarantineSlot(slot, 'boot-recovery barrier timed out — slot unresolved, fail-closed');
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
/* @silent-fallback-ok — ledger may be UNKNOWN-mode; the barrier still lifts so the balancer can run on the healthy remainder */
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
lift();
|
|
432
|
+
}, this.recoveryBarrierTimeoutMs);
|
|
433
|
+
if (typeof barrierTimer.unref === 'function')
|
|
434
|
+
barrierTimer.unref();
|
|
435
|
+
try {
|
|
436
|
+
await this.funnel.withSingleMover(async () => {
|
|
437
|
+
for (const entry of inFlight) {
|
|
438
|
+
await this.recoverEntry(entry);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
finally {
|
|
443
|
+
clearTimeout(barrierTimer);
|
|
444
|
+
lift();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/** In-flight swap journal rows = a `begin`/`exchanged`/`verified` row whose swapId has no later `done`/`aborted`. */
|
|
448
|
+
inFlightSwaps() {
|
|
449
|
+
const bySwap = new Map();
|
|
450
|
+
for (const e of this.ledger.getJournal()) {
|
|
451
|
+
if (e.op !== 'swap')
|
|
452
|
+
continue;
|
|
453
|
+
const id = parseSwapId(e.detail);
|
|
454
|
+
if (!id)
|
|
455
|
+
continue;
|
|
456
|
+
const rec = bySwap.get(id) ?? { slots: e.slots, phases: new Set() };
|
|
457
|
+
rec.phases.add(e.phase);
|
|
458
|
+
if (e.slots.length)
|
|
459
|
+
rec.slots = e.slots;
|
|
460
|
+
bySwap.set(id, rec);
|
|
461
|
+
}
|
|
462
|
+
const out = [];
|
|
463
|
+
for (const [swapId, rec] of bySwap) {
|
|
464
|
+
const terminal = rec.phases.has('done') || rec.phases.has('aborted');
|
|
465
|
+
if (terminal)
|
|
466
|
+
continue;
|
|
467
|
+
const lastPhase = rec.phases.has('verified') ? 'verified' : rec.phases.has('exchanged') ? 'exchanged' : 'begin';
|
|
468
|
+
out.push({ swapId, slots: rec.slots, lastPhase });
|
|
469
|
+
}
|
|
470
|
+
return out;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Resolve one in-flight entry. Per §2.3 recovery decidability: re-read each slot, apply
|
|
474
|
+
* adopt-on-newer (never a blind staging overwrite), and verify identity; an unverifiable slot is
|
|
475
|
+
* quarantined (never repaired blindly). Staging is deleted + the row closed `done` ONLY when
|
|
476
|
+
* both slots verify; otherwise the row stays in-flight and its staging is retained (the sweep
|
|
477
|
+
* predicate protects any non-`done` row).
|
|
478
|
+
*/
|
|
479
|
+
async recoverEntry(entry) {
|
|
480
|
+
const [slotA, slotB] = entry.slots;
|
|
481
|
+
if (!slotA || !slotB) {
|
|
482
|
+
// Cannot resolve a malformed entry; close it aborted (nothing to heal that we can identify).
|
|
483
|
+
this.ledger.appendJournal({ op: 'swap', phase: 'aborted', slots: entry.slots, detail: `swapId=${entry.swapId} unrecoverable-entry` });
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
// The ledger's intended tenants after this swap are the CURRENT assignments (recordAssignment
|
|
487
|
+
// ran iff the swap committed); for an in-flight entry we resolve by reading the live identity
|
|
488
|
+
// of each slot and quarantining anything we cannot confirm. We never guess a tenant.
|
|
489
|
+
const staging = this.stagingService(entry.swapId);
|
|
490
|
+
let allConfirmed = true;
|
|
491
|
+
for (const slot of [slotA, slotB]) {
|
|
492
|
+
const id = await this.resolveIdentity(slot);
|
|
493
|
+
if ('unavailable' in id) {
|
|
494
|
+
allConfirmed = false;
|
|
495
|
+
this.ledger.quarantineSlot(slot, scrub(`boot-recovery: oracle unavailable (${id.reason})`));
|
|
496
|
+
await this.raiseQuarantineAttention(slot, entry.swapId, 'boot-recovery oracle unavailable');
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
if (allConfirmed) {
|
|
500
|
+
// Both slots carry a confirmable tenant → the exchange is coherent; clean up staging + close.
|
|
501
|
+
await this.keychain.deleteService(staging);
|
|
502
|
+
this.ledger.appendJournal({ op: 'swap', phase: 'done', slots: entry.slots, detail: `swapId=${entry.swapId} recovered` });
|
|
503
|
+
this.audit(entry.swapId, 'recovered', slotA, slotB, 'boot-recovery confirmed both slots');
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
// Leave the row in-flight (staging retained — the heal source). The slot(s) are quarantined
|
|
507
|
+
// and excluded from balancing until a clean re-probe; staging is NOT deleted.
|
|
508
|
+
this.audit(entry.swapId, 'recover-deferred', slotA, slotB, 'a slot unconfirmed — staging retained, slots quarantined');
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
// ── Orphan-staging sweep (boot) ───────────────────────────────────────────────────────────────
|
|
512
|
+
/**
|
|
513
|
+
* A staging entry is an ORPHAN iff its swapId has NO journal row, OR its row is `done`. Any
|
|
514
|
+
* non-`done` row (begin/exchanged/verified) PROTECTS its staging (the heal source through step
|
|
515
|
+
* 6 — a literal "in-flight = begin only" reading would delete a committed row's heal source).
|
|
516
|
+
* Caller supplies the live staging-service list (the executor never enumerates the keychain).
|
|
517
|
+
*/
|
|
518
|
+
async sweepOrphanStaging(stagingServices) {
|
|
519
|
+
// A swapId is PROTECTED iff it is currently in-flight — i.e. it has at least one swap journal
|
|
520
|
+
// row and NONE of them is terminal (`done`/`aborted`). `inFlightSwaps()` computes exactly that
|
|
521
|
+
// (a swapId with ANY `done` row drops out). A `done`/`aborted` swapId and a swapId with no row
|
|
522
|
+
// at all are both UNPROTECTED → their staging is an orphan and is deleted. (The earlier
|
|
523
|
+
// per-row "protect on any non-done row" reading would wrongly retain a finished swap's staging
|
|
524
|
+
// whose history still carries its `begin` row — the lost-source bug this predicate avoids in
|
|
525
|
+
// the other direction.)
|
|
526
|
+
const inFlightIds = new Set(this.inFlightSwaps().map((e) => e.swapId));
|
|
527
|
+
const deleted = [];
|
|
528
|
+
for (const service of stagingServices) {
|
|
529
|
+
if (!service.startsWith(STAGING_SERVICE_PREFIX))
|
|
530
|
+
continue;
|
|
531
|
+
const id = service.slice(STAGING_SERVICE_PREFIX.length);
|
|
532
|
+
if (inFlightIds.has(id))
|
|
533
|
+
continue;
|
|
534
|
+
await this.keychain.deleteService(service);
|
|
535
|
+
deleted.push(service);
|
|
536
|
+
}
|
|
537
|
+
return deleted;
|
|
538
|
+
}
|
|
539
|
+
// ── Blob read + validation ────────────────────────────────────────────────────────────────────
|
|
540
|
+
/** Read a slot's blob via the async keychain exec; require it parse + carry a refresh token. */
|
|
541
|
+
async readBlob(slot) {
|
|
542
|
+
const raw = await this.keychain.readService(claudeCredentialService(slot));
|
|
543
|
+
if (!raw)
|
|
544
|
+
return null;
|
|
545
|
+
let parsed;
|
|
546
|
+
try {
|
|
547
|
+
parsed = JSON.parse(raw);
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
return null; // @silent-fallback-ok: unparseable blob → precondition fail (never overwritten); the caller refuses the swap
|
|
551
|
+
}
|
|
552
|
+
const oauth = (parsed?.claudeAiOauth ?? null);
|
|
553
|
+
if (!oauth || typeof oauth !== 'object')
|
|
554
|
+
return null;
|
|
555
|
+
if (typeof oauth.refreshToken !== 'string' || !oauth.refreshToken)
|
|
556
|
+
return null;
|
|
557
|
+
return { raw, oauth };
|
|
558
|
+
}
|
|
559
|
+
async raiseQuarantineAttention(slot, swapId, why) {
|
|
560
|
+
if (!this.emitAttention)
|
|
561
|
+
return;
|
|
562
|
+
try {
|
|
563
|
+
await this.emitAttention({
|
|
564
|
+
id: `credential-swap-quarantine:${credentialSlotKey(slot)}`,
|
|
565
|
+
title: 'A credential slot was quarantined during a swap',
|
|
566
|
+
summary: scrub(`Slot ${expandHome(slot)} was quarantined (${why}); it is excluded from balancing until a clean re-probe. Blast radius: one account may need re-auth.`),
|
|
567
|
+
category: 'credential-repointing',
|
|
568
|
+
priority: 'HIGH',
|
|
569
|
+
sourceContext: 'credential-swap-executor',
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
// @silent-fallback-ok — best-effort notice; the quarantine (the safety action) is already
|
|
574
|
+
// durably recorded in the ledger whether or not this notice is delivered.
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
// ─── Module helpers ──────────────────────────────────────────────────────────────────────────────
|
|
579
|
+
/** Extract `swapId=<id>` from a journal detail string. */
|
|
580
|
+
function parseSwapId(detail) {
|
|
581
|
+
if (!detail)
|
|
582
|
+
return null;
|
|
583
|
+
const m = /swapId=([^\s]+)/.exec(detail);
|
|
584
|
+
return m ? m[1] : null;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Scrub any token material from a free-text string before it reaches a persisted/served/notified
|
|
588
|
+
* surface (§2.9 single-emit chokepoint). Catches the `sk-ant-…` family via `redactToken` on any
|
|
589
|
+
* token-shaped run, so a `security` stderr or a `${raw}`-bearing interpolation can't leak a byte.
|
|
590
|
+
*/
|
|
591
|
+
function scrub(s) {
|
|
592
|
+
if (!s)
|
|
593
|
+
return s;
|
|
594
|
+
return s.replace(/sk-ant-[A-Za-z0-9_-]+/g, (m) => redactToken(m));
|
|
595
|
+
}
|
|
596
|
+
//# sourceMappingURL=CredentialSwapExecutor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CredentialSwapExecutor.js","sourceRoot":"","sources":["../../src/core/CredentialSwapExecutor.ts"],"names":[],"mappings":"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"}
|