// ⟨q-0c5476ec⟩ — A SEAT'S IDENTITY IS STATED ONCE, AND THE TWO HALVES CANNOT DISAGREE SILENTLY. // // A seat's identity was typed twice: as an agent id at launch, and as a bearer inside a config. // Every HTTP identity failure so far is that duplication. Measured on the other machine: a seat // launched WITHOUT `--strict-mcp-config` took the GLOBAL config's bearer and came back as whoever // owned it — three times in two days on three seats, with no in-session recovery; and a config // holding a token `tokens.json` had since rotated returned a bare 401, with nothing in the config // saying it was stale. // // So the launcher derives BOTH halves from one statement (the agent id), and refuses before start // when a config on disk disagrees, NAMING WHICH HALF disagreed. Refusing at launch is the point: a // 401 later is the same fault read as a broken daemon. // // ⛔ NO SECRET IN ANY MESSAGE. Not the token, not a prefix, not a hash: a refusal a human pastes // into a bus message must not carry the bearer. The messages name AGENTS and FILES, never bytes. import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; import path from "node:path"; /** Must match store.ts ROOT/TOKENS_FILE resolution exactly, or we read a file the bus never wrote. */ export function coordRoot({ dir, env = process.env }: { dir?: string; env?: NodeJS.ProcessEnv } = {}): string { return dir ?? env.AGENT_COORD_DIR ?? env.CLAUDE_COORD_DIR ?? path.join(homedir(), "agent-coord"); } export const tokensFile = (opts?: { dir?: string; env?: NodeJS.ProcessEnv }) => path.join(coordRoot(opts), "tokens.json"); /** `{ "agent-id": "token" }`, or `{}` when absent. Throws on malformed, never on missing. */ export function loadTokens(file: string): Record { if (!existsSync(file)) return {}; const parsed: unknown = JSON.parse(readFileSync(file, "utf8")); if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) { throw new Error(`${file} must be a JSON object of { "agent-id": "token" }`); } return parsed as Record; } /** * Write a project-local `.mcp.json` (⟨q-8c23e5a3⟩ acceptance (a)) whose `agent-coord` entry embeds * this seat's own token — never a shared default, so a project that gets its OWN config never * depends on (or silently becomes) a global fallback. mode 600, same as tokens.json. * `pane` is recorded under `_agentCoordPane`, a key no MCP client reads or writes: it exists so a * re-run, or `coord-seat`, can recover the pane a config was minted for without re-asking. */ export function writeMcpConfig({ dir, agentId, server, token, pane }: { dir: string; agentId: string; server: string; token: string; pane?: string | null }): string { mkdirSync(dir, { recursive: true }); const configPath = path.join(dir, ".mcp.json"); let existing: Record = {}; if (existsSync(configPath)) { try { existing = JSON.parse(readFileSync(configPath, "utf8")); } catch (e) { throw new Error(`${configPath} exists and is not valid JSON (${(e as Error).message}) — refusing to overwrite blindly`); } } const config: Record = { ...existing, mcpServers: { ...(existing.mcpServers ?? {}), "agent-coord": { type: "http", url: server, headers: { Authorization: `Bearer ${token}` }, ...(pane ? { _agentCoordPane: pane } : {}), }, }, }; writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", { mode: 0o600 }); chmodSync(configPath, 0o600); return configPath; } /** The agent-coord entry a config states, or null when there is no config / no such entry. */ export type ConfigIdentity = { token: string | null; url: string | null; pane: string | null }; export function readConfigIdentity(configPath: string): ConfigIdentity | null { if (!existsSync(configPath)) return null; let parsed: any; try { parsed = JSON.parse(readFileSync(configPath, "utf8")); } catch (e) { throw new Error(`${configPath} is not valid JSON (${(e as Error).message}) — refusing to read an identity out of it`); } const entry = parsed?.mcpServers?.["agent-coord"]; if (!entry) return null; const auth = String(entry.headers?.Authorization ?? ""); const m = /^Bearer\s+(.+)$/.exec(auth); return { token: m ? m[1].trim() : null, url: entry.url ?? null, pane: entry._agentCoordPane ?? null }; } /** * Does the config on disk state the same seat the launch claims? * * Returns null when they agree (or when there is no config to disagree — the launcher writes one * from the single statement). Otherwise `{ half, message }`, where `half` names the side that is * wrong relative to `tokens.json`, which is the bus's own map and therefore the authority. */ export type IdentityProblem = { half: "config" | "tokens"; message: string }; export function identityProblem({ agentId, configPath, config, tokens, tokensPath }: { agentId: string; configPath: string; config: { token?: string | null } | null; tokens: Record; tokensPath: string }): IdentityProblem | null { const expected = tokens?.[agentId]; if (!config) return null; if (!config.token) { return { half: "config", message: `${configPath} has an agent-coord entry with no bearer, so it states no identity, while the launch claims '${agentId}'. ` + `Delete it and relaunch through coord-seat, which writes both halves from the one statement.`, }; } if (!expected) { return { half: "tokens", message: `${tokensPath} holds no token for '${agentId}', so the launch identity cannot be honoured, while ${configPath} carries a bearer. ` + `Mint one with \`coord-token add ${agentId}\` (it rotates if one exists) and relaunch, or launch the agent whose token that config holds.`, }; } if (config.token === expected) return null; const ownerOfConfigToken = Object.keys(tokens).find((a) => tokens[a] === config.token); return { half: "config", message: ownerOfConfigToken ? `${configPath} carries the bearer of '${ownerOfConfigToken}', but the launch claims '${agentId}' — the two halves of this seat's identity disagree. ` + `Launch '${ownerOfConfigToken}', or let coord-seat rewrite the config for '${agentId}'.` : `${configPath} carries a bearer that ${tokensPath} does not know — it was rotated or revoked since the config was written, while the launch claims '${agentId}'. ` + `Relaunch through coord-seat, which rewrites the config from the token '${agentId}' has now.`, }; }