Security model
Sealmesh reads what coding agents are doing on people's own machines and shows it to their teammates. What it handles isn't a database: it's what people are working on, including work they haven't shared. This page states what's protected and what isn't yet.
What leaves a machine
Per agent, only typed fields: job id, repository slug, run state, branch, ticket id, commits ahead, uncommitted file count, completed/total tasks, pull request number, and two timestamps.
- No text a person or agent wrote. Every field on the wire is an enum, a number, a timestamp, or a newtype whose constructor enforces a charset and a length. Claude Code's
intentanddetailhold the raw prompt; they're dropped where the file is read. A canary test plants a prompt in every free-text field and fails the build if it appears in the output. Getting free text onto the wire is the highest-severity bug this project can have. - No filesystem paths. A repository is named by its remote slug.
- Only allowlisted repositories and paths. Anything else produces no output, not even the fact that it exists.
What is kept, and where
| Where | What | How long |
|---|---|---|
| Router, disk | Room chat: room, sender host, kind, body | 7 days, swept hourly. Only with chat enabled |
| Router, memory | Live connections and each one's GitHub token; names of hosts seen since start; access cache | Until disconnect or restart; cache 30–300 s |
| Router, logs | GitHub logins and project ids on stderr | Whatever your log setup keeps |
| Your machine | audit.jsonl (who asked, when); PR/CI cache; declared-agent records | Until you delete them |
The router never stores a snapshot, an agent row or a who answer. A machine that disconnects leaves nothing behind.
Held by design
- Identity comes from the credential. The host a connection speaks for is derived from the GitHub login its token resolves to, never from a path, a body or config.
- Membership is checked live. Only active members of the router's organization can connect. Removing someone from the org removes their access.
- Readers see only what they could read anyway. Every
whoand every room is cut down to repositories the reader's own token can read. - A panic is a denial of service.
unsafeis forbidden, andunwrap,expect,panicand slice indexing are denied outside tests. - Plaintext is refused to anything but loopback, on both the daemon and the router.
Known and not yet held
These are recorded rather than hidden.
- A compromised router is a remote read trigger. Whoever controls the router can make every connected machine run its collector. The allowlists bound what that reaches and the per-machine rate limit bounds how often, but the trigger is inherent to answering on demand.
- The daemon can't verify who is asking.
asked_byis the router's claim, so a compromised router could attribute a query to someone who never made it.sealmesh auditshows it as claimed. Signing by the requester would close this; it isn't built. - The GitHub token is broader than the router needs.
gh auth tokenhands over whatever you authorized, typically includingrepo, while the router needsread:organd repo read. The router holds it in memory for the life of the connection, never on disk or in logs. A GitHub App device flow issuing a narrower token is the planned fix.
Reporting a vulnerability
Privately, through GitHub security advisories, or by email to security@sealmesh.dev. See Security.