Skip to content

Security: stelis-dev/code-chan

Security

docs/SECURITY.md

Security and privacy

This document owns code-chan's trust boundaries, the bounded data shapes that cross them, and the reporting path. AGENTS.md owns the policy that forbids sending workspace data to the device; this document records which bounded shapes were actually accepted and what each boundary currently admits.

Statements here describe current implementation. Where a limit or message shape is named, the owning document — PROTOCOL.md for message semantics, HOST_BRIDGE.md for host behavior — remains authoritative.

Threat model

code-chan runs entirely on one machine and one USB cable. It has no server, no account, no telemetry, and no network listener reachable from outside the host.

The assets worth protecting are the developer's workspace contents — prompts, source, command strings, file paths, conversation history — and the authority to approve a coding agent's tool use. The boundaries below exist to keep the first away from the device and to keep the second correlated to a real request.

An attacker who already runs code as the developer's user account is outside this model. Such an attacker can read the workspace directly and does not need code-chan; the loopback listener grants no authority the attacker lacks.

Trust boundaries

Coding agent to host bridge

HOST_BRIDGE.md owns the ingress behavior and the listener-ownership rule. The security-relevant shape of it is that the listener binds the loopback interface explicitly, so it is not reachable from another host, and that the ingress accepts POST only, on an allowlist of exact paths, under a body cap enforced while the body streams, admitting a body only if it decodes as strict UTF-8 and parses to a JSON object.

The endpoint carries no authentication. Any process running as the same user can post to it, and that is the accepted boundary: such a process already has the developer's privileges. What the endpoint does not offer is an escalation — a posted decision is admitted only under the correlation rules below.

Host bridge to device

HOST_BRIDGE.md owns what the relay strips and what the host may send; PROTOCOL.md owns the message vocabulary and states that the protocol never carries the raw command, prompt, file path, source text, or tool-input object. Neither rule is restated here.

What this document adds is why the rule is structural rather than a discipline that could lapse. The vocabulary offers nowhere to put such content: outside enums, bounded counts, usage percentages, and identifiers, the only free-text fields in the whole contract are the two optional labels on a decision request, and one of them is populated by no caller at all. No producer adapter reads a tool's input, so no source for the content exists even where a field does. A regression would therefore have to add both a field and a reader, which the contract check and the protocol generator make visible rather than silent.

Identifiers on the wire derive from the coding agent's own opaque ids, not from content, so a captured transcript of the serial link reveals which turn was running and never what it was doing.

Claude status line to host bridge

The optional status line is the one path that receives a payload code-chan does not define. Claude Code invokes the status-line command with a session JSON object on standard input; the relay accepts up to 256 KiB of it and treats every field it did not ask for as data to be discarded.

The relay reduces that object to {sessionId, promptId, usage} — two opaque ids and up to three integers in 0..100before anything leaves the local command. It reads only the version, the two ids, and the usage metrics. Whatever else the object carries is never sent, logged, or retained, so the boundary holds without depending on which fields the coding agent includes.

The host does not trust that reduction. At ingress it independently re-validates the exact shape, rejecting any object whose key set differs from the three expected keys or whose metrics are not integers in range.

Device to host bridge

The device returns acknowledgements, runtime status, touch events, and decisions. It reports nothing about its own display or body state.

A decision is admitted only when the coordinator is running a turn, the turn id matches the running turn, and the request id matches the permission request currently pending. A decision for a stale, absent, or mismatched request is refused. Body touch is telemetry: it cannot wake the screen, select a foreground session, or answer a permission request.

A permission timeout leaves the coding agent's own permission flow in control. The device never approves by default and never approves by inaction.

Supply chain

The host bridge has zero runtime dependencies. The only installed packages are development-time: typescript, @types/node, and undici-types. npm audit --omit=dev reports no vulnerabilities.

The firmware links no network stack. It has no Wi-Fi, Bluetooth, OTA, or TLS code path, and no credential of any kind is compiled into it. Its only external interface is the USB serial connection.

What is deliberately kept out of the repository

.gitignore excludes .env*, *.pem, *.key, and the .WORK/ working directory, which holds plans, progress notes, and hardware identifiers such as the development board's serial number.

Tracked hook configuration resolves paths at run time — $CLAUDE_PROJECT_DIR in .claude/settings.json and git rev-parse --show-toplevel in .codex/hooks.json — so no contributor's absolute path is committed. The personal status-line installation is written to the ignored .claude/settings.local.json.

docs/README.md states the rule this section implements: public documents stay free of credentials, private captures, local absolute paths, and user-specific configuration.

Reporting a vulnerability

Report suspected vulnerabilities through a GitHub security advisory on stelis-dev/code-chan rather than a public issue. This is a personal project with no service component and no security SLA; expect best-effort response.

Non-goals

  • code-chan is not a security control. It reports agent activity and relays one explicit permission choice; it does not sandbox, audit, or restrict what a coding agent does.
  • The loopback endpoint is not an authentication boundary between processes on the same machine, and no future feature should treat it as one.
  • No remote or cross-host operation is supported. Should a wireless transport be added, its authentication and confidentiality must be decided in this document before the transport is implemented.

There aren't any published security advisories