cyberine
Design notes

Bus/Presence/MCP Auth Scope (T584)

Finding

apiTokenIsValid (src/store/apiTokens.ts) is wired into exactly one place: the REST authorized() check in src/web/security.ts, which is itself skipped entirely when the web server is bound to loopback (isLoopbackBind(), see enforceWebSecurity). The team-bus/presence/MCP surface — src/store/busTools.ts (sendBusMessage, pollBusInbox, queryRuns, listTeam, sendToTeam), src/store/presence.ts (announcePresence, heartbeatPresence, tombstonePresence, roster, resolveRecords/resolveAddr), and src/mcp.ts's registered tools (send_message, poll_inbox, query_runs, list_team) — has zero reference to apiTokens. Any local process can publish a bus envelope or a presence record and have it rendered/acted on by any other local participant (the TUI's /team view, pollTeamInboxNow, another chat session, another MCP client) with no identity check whatsoever.

Update 2026-09-22: the src/mcp.ts tools now have an opt-in CYBERINE_MCP_AUTH_TOKEN gate (T602), fail-open when the env var is unset, see src/mcp.ts:63-86. The underlying busTools.ts/presence.ts store functions still take no caller identity, so the residual gap is those functions, not "every MCP tool".

Decision: defer (no auth wiring)

This repo is explicitly in Sandbox stage (.claude/rules/user-vibe-stage.md): not deployed anywhere, local/dev only, no production exists, single operator. Given that:

  1. The trust boundary is already the operator's own shell, not a network boundary. The bus, presence directory, and MCP stdio transport all live under $CYBERINE_HOME on the local filesystem (src/store/messageBus.ts, src/store/presence.ts's busRoot()/presenceDir()), and the MCP server (src/mcp.ts) is a stdio process spawned directly by the host (Claude Code, Cursor, Claude Desktop) — never exposed over a socket. Anything able to write to that bus/presence directory or spawn the MCP process already has the same OS-level access as the operator's own terminal. A token check here would gate a boundary that does not exist yet.
  2. enforceWebSecurity already establishes the precedent that this repo does NOT authenticate loopback-local traffic — isLoopbackBind() short-circuits the one place apiTokenIsValid IS wired in. The bus/presence/MCP surface is at least as local as a loopback HTTP bind (it never leaves the filesystem/stdio), so treating it as unauthenticated is consistent with, not an exception to, the existing security posture.
  3. The real multi-host teaming feature (T490) hasn't landed. Today every participant on the bus is a process the same operator started, on the same machine. Auth only starts to matter once the bus (or an MCP endpoint) can be reached by a process the operator did NOT start — e.g. a remote teammate, a shared multi-host $CYBERINE_HOME, or an MCP transport exposed over a network socket instead of stdio. Building token checks now, before that shape exists, is friction (every local dev/test invocation would need a token minted and threaded through sendBusMessage/announcePresence/every MCP tool call) with no corresponding gain: the only additional thing an "attacker" could do today is act as a process the operator already trusts to run on their own machine.
  4. Existing hardening already covers the realistic local-abuse case. T568 (security(bus): sanitize presence+envelope in /team paths, see scripts/t568-smoke.ts) already sanitizes rendered presence/envelope fields against a malformed-or-hostile LOCAL writer (a buggy or unusual process on the same bus), which is the actual risk this stage faces — a garbage/oversized field crashing or corrupting the TUI render — not impersonation across a trust boundary that doesn't exist yet.

Re-open trigger

Re-open this decision (and default to "build": require apiTokenIsValid, or an equivalent allowlist, on sendBusMessage/announcePresence/the MCP tool handlers) as soon as ANY of:

  • T490 (multi-host teaming) ships and $CYBERINE_HOME/the bus becomes reachable from a process the operator did not start.
  • The MCP server gains a network transport (SSE/HTTP) instead of stdio-only.
  • The project's stage ladder moves off early-dev (see user-vibe-stage.md) — i.e. any live deploy with real or shared users.

Non-decision

This note does not touch REST API auth (src/web/security.ts) — that path already has apiTokenIsValid wired in and is out of scope for T584.