The first vendor binding of model-access (ADR 0050): anthropic-manager (seals the operator refresh token to the node public key at adopt, refreshes against Anthropic's OAuth endpoint, re-seals a rotated token, emits only the access token + opaque box + licence-grain usage) and anthropic-consumer (writes ~/.claude/.credentials.json access-token-only, reports session-grain usage from the CLI transcripts per ADR 0054).
sealedbox.ts delegates to the audited tweetnacl-sealedbox-js (libsodium crypto_box_seal wire format — the Go host still opens it; cross-check proven). The manager declares tweetnacl-sealedbox-js/tweetnacl as runtime deps.
Proven end to end by the mesh-lab anthropic-bed suite (green).
The first vendor binding of model-access (ADR 0050): `anthropic-manager` (seals the operator refresh token to the node public key at adopt, refreshes against Anthropic's OAuth endpoint, re-seals a rotated token, emits only the access token + opaque box + licence-grain usage) and `anthropic-consumer` (writes `~/.claude/.credentials.json` access-token-only, reports session-grain usage from the CLI transcripts per ADR 0054).
`sealedbox.ts` delegates to the audited `tweetnacl-sealedbox-js` (libsodium `crypto_box_seal` wire format — the Go host still opens it; cross-check proven). The manager declares `tweetnacl-sealedbox-js`/`tweetnacl` as runtime deps.
Proven end to end by the mesh-lab `anthropic-bed` suite (green).
Phase C of vendor-agnostic model-access (ADR 0050/0054). Two TypeScript
runtime modules:
- anthropic-manager: the refresh token is sealed at rest to the manager
node's own key (atrest.ts, envelope encryption over X25519) and opened
ONLY on the manager node. adopt seals the first envelope; refresh opens
it, calls the Anthropic OAuth token endpoint, re-seals a rotated refresh
token, and hands the control plane only the access token plus the opaque
envelope. Also polls licence-grain usage (ADR 0054).
- anthropic-consumer: writes the delivered access token to
~/.claude/.credentials.json, access-token-only, atomically (the refresh
token is never delivered); reports session-grain usage from the CLI
transcripts; a fail-closed identity guard (expected-uuid plumbing is a
flagged TODO).
Both run as scheduled containers (ADR 0053). Pure logic covered by
node --test fixtures (at-rest round-trip, credential strip, transcript
sum, refresh merge).
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
The manager module drops its bespoke ECIES at-rest envelope and the node-private-key mount.
A module is never given a node's private key, so it cannot open an envelope -- the refresh
token is now delivered to it as cleartext by the host, unsealed from an ordinary sealed box.
- sealedbox.ts: a dependency-free NaCl crypto_box_seal (node:crypto for X25519, transcribed
XSalsa20-Poly1305 and BLAKE2b-24), byte-compatible with Go's box.SealAnonymous. It SEALS
only -- opening is the host's job. Proven by a cross-language test in mesh-control.
- adopt: reads the node's PUBLIC key from the delivered bound facts and seals the operator's
refresh token to it, handing out only the box.
- refresh: reads the refresh token as cleartext the host mounted, calls the vendor, re-seals
a rotated token to the node's public key, submits only { access token, box }.
- module.json: a model-access holder now -- binds the facts, binds the refresh token as a
sealed secret; no keys dir, no MESH_NODE_SEALING_* mount.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
The manager reseals a rotated refresh token to the node key with crypto_box_seal. That
seal was a full inline transcription of TweetNaCl's XSalsa20-Poly1305 and blakejs' BLAKE2b
(dependency-free, ~440 lines). Replace the internals with the audited
tweetnacl-sealedbox-js library — the same crypto_box_seal, on the same tweetnacl and blakejs
the mesh used to validate the seal during Phase C.
The exported API is unchanged: seal(value, recipientPublicB64) -> base64. The wire format is
unchanged too — ephemeralPub(32) followed by the box, nonce = blake2b(ephemeralPub +
recipientPub, 24) — so the host's Go box.OpenAnonymous still opens it. The mesh-control
cross-check fixture is regenerated from this seal().
The library and tweetnacl are added to the module's package.json dependencies so the runtime
image bundles them (blakejs arrives transitively). A local ambient .d.ts types the untyped
CJS bundle; it is imported as a default import because Node's ESM loader cannot see a UMD
bundle's named exports.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The first vendor binding of model-access (ADR 0050):
anthropic-manager(seals the operator refresh token to the node public key at adopt, refreshes against Anthropic's OAuth endpoint, re-seals a rotated token, emits only the access token + opaque box + licence-grain usage) andanthropic-consumer(writes~/.claude/.credentials.jsonaccess-token-only, reports session-grain usage from the CLI transcripts per ADR 0054).sealedbox.tsdelegates to the auditedtweetnacl-sealedbox-js(libsodiumcrypto_box_sealwire format — the Go host still opens it; cross-check proven). The manager declarestweetnacl-sealedbox-js/tweetnaclas runtime deps.Proven end to end by the mesh-lab
anthropic-bedsuite (green).The manager module drops its bespoke ECIES at-rest envelope and the node-private-key mount. A module is never given a node's private key, so it cannot open an envelope -- the refresh token is now delivered to it as cleartext by the host, unsealed from an ordinary sealed box. - sealedbox.ts: a dependency-free NaCl crypto_box_seal (node:crypto for X25519, transcribed XSalsa20-Poly1305 and BLAKE2b-24), byte-compatible with Go's box.SealAnonymous. It SEALS only -- opening is the host's job. Proven by a cross-language test in mesh-control. - adopt: reads the node's PUBLIC key from the delivered bound facts and seals the operator's refresh token to it, handing out only the box. - refresh: reads the refresh token as cleartext the host mounted, calls the vendor, re-seals a rotated token to the node's public key, submits only { access token, box }. - module.json: a model-access holder now -- binds the facts, binds the refresh token as a sealed secret; no keys dir, no MESH_NODE_SEALING_* mount. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF