// The manager's scheduled run (novox/hq ADR 0050/0053). It is the whole of the carve-out in one // place, and it runs on the MANAGER NODE, never in the control plane: // // 1. read the opaque refresh-token envelope the control plane forwarded (it cannot open it); // 2. open it HERE with the node's own sealing key — the one moment a refresh token is in the clear, // on the one node the ADR permits it; // 3. call the vendor's OAuth token endpoint to mint a fresh access token (and maybe a rotated // refresh token); // 4. re-seal the rotated refresh token at rest (still openable by this node alone); // 5. hand the control plane back ONLY the access token in the clear + the opaque re-sealed // envelope — never the refresh token — which it seals per holder and stores; // 6. poll usage with the fresh access token and record the licence-grain reading. // // mesh-control receives the products of steps 5–6 through `licence submit-refresh` (access token + // opaque envelope). The refresh token never leaves this process except as ciphertext. // // This runs as `mesh-tools run`, which connects no broker, so the outputs are written to files the // host mounts; the submit itself (the transport to mesh-control) is done by the caller invoking // `mesh-control licence submit-refresh`. In the lab that caller is the scenario; in production it is // an authenticated call the manager node makes. The transport is the one part stubbed here — FLAGGED // — because a cross-node authenticated command surface is out of this module's scope. import { readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs"; import { dirname } from "node:path"; import { openAtRest, sealAtRest, type Envelope } from "../atrest.js"; import { refreshGrant, grantFromRefresh, readUsage, flattenUsage } from "../client.js"; function required(name: string): string { const v = process.env[name]; if (!v) throw new Error(`${name} is not set — the manager runtime was deployed without it`); return v; } function readTrimmed(path: string): string { return readFileSync(path, "utf8").trim(); } /** Accept an envelope in either the wire (snake_case) or internal (camelCase) shape. */ function readEnvelope(path: string): Envelope { const raw = JSON.parse(readFileSync(path, "utf8")) as Record; const token = raw.token ?? ""; const wrappedKey = raw.wrappedKey ?? raw.wrapped_key ?? ""; const managerKey = raw.managerKey ?? raw.manager_key ?? ""; if (!token || !wrappedKey || !managerKey) { throw new Error("the refresh-token envelope is missing one of token/wrapped_key/manager_key"); } return { token, wrappedKey, managerKey }; } /** Write the envelope in the wire (snake_case) shape mesh-control's `submit-refresh` reads. */ function writeEnvelope(path: string, env: Envelope): void { atomicWrite( path, JSON.stringify({ token: env.token, wrapped_key: env.wrappedKey, manager_key: env.managerKey }), ); } function atomicWrite(path: string, content: string): void { mkdirSync(dirname(path), { recursive: true }); const tmp = `${path}.tmp`; writeFileSync(tmp, content, { mode: 0o600 }); renameSync(tmp, path); } async function main(): Promise { const licence = process.env.MESH_ANTHROPIC_LICENCE ?? "unknown"; const envelope = readEnvelope(required("MESH_ANTHROPIC_GRANT_FILE")); const nodePub = readTrimmed(required("MESH_NODE_SEALING_PUBLIC_FILE")); const nodePriv = readTrimmed(required("MESH_NODE_SEALING_PRIVATE_FILE")); // Step 2: the one open, on the manager node. const refreshToken = openAtRest(envelope, nodePub, nodePriv); // Step 3: the vendor call. const refreshed = await refreshGrant(refreshToken); if (!refreshed) { // A dead endpoint or a rejected token: nothing to publish, and we do not clobber a good grant. throw new Error(`[anthropic-manager] the refresh of ${licence} produced no grant`); } const grant = grantFromRefresh(refreshed, Date.now()); if (!grant) { throw new Error(`[anthropic-manager] the refresh of ${licence} returned no access token`); } // Step 4: re-seal the rotated refresh token, if the vendor rotated it. Nothing to store otherwise. if (grant.rotatedRefresh) { const rotated = sealAtRest(grant.rotatedRefresh, nodePub); if (process.env.MESH_ANTHROPIC_GRANT_OUT) { writeEnvelope(process.env.MESH_ANTHROPIC_GRANT_OUT, rotated); } } // Step 5: the access token in the clear, for the control plane to seal per holder. This is all it // ever receives that is not ciphertext. atomicWrite(required("MESH_ANTHROPIC_ACCESS_OUT"), grant.access.accessToken); console.error( `[anthropic-manager] refreshed ${licence}: access token minted` + (grant.rotatedRefresh ? ", refresh token rotated and re-sealed" : ", refresh token unchanged"), ); // Step 6: licence-grain usage, best-effort — a usage read failing must not fail the refresh. try { const usage = await readUsage(grant.access.accessToken); if (usage) { const reading = flattenUsage(usage); if (process.env.MESH_ANTHROPIC_USAGE_OUT) { atomicWrite( process.env.MESH_ANTHROPIC_USAGE_OUT, JSON.stringify({ licence, grain: "licence", ...reading }), ); } await emitUsage({ licence, grain: "licence", ...reading }); } } catch (err) { console.error(`[anthropic-manager] usage poll for ${licence} failed: ${err}`); } } /** * Emit a usage event best-effort by shelling out to the sibling mesh-tools `emit` primitive, which * is the one path that wires a broker from a run-once/scheduled step (which itself connects none). * A broker hiccup must never fail a refresh that already happened. */ async function emitUsage(body: Record): Promise { const main = process.env.MESH_TOOLS_MAIN ?? "/app/dist/main.js"; const { spawn } = await import("node:child_process"); await new Promise((resolve) => { const child = spawn(process.execPath, [main, "emit", "module.anthropic-manager.usage.read", JSON.stringify(body)], { stdio: "inherit", }); child.on("exit", () => resolve()); child.on("error", (err) => { console.error(`[anthropic-manager] could not emit usage: ${err}`); resolve(); }); }); } await main();