Security model
Tesseron's security model is local-first, user-authorised. The MCP gateway binds to localhost and won't expose any action until a human types a short code out-of-band. These are the two gates.
Gate 1 - loopback-only discovery
Section titled “Gate 1 - loopback-only discovery”Apps bind locally only - WebSocket servers on 127.0.0.1, Unix domain sockets in private temp dirs. The gateway refuses non-loopback URLs read from ~/.tesseron/instances/ and rejects UDS paths it can't connect() to as the running user. The gateway itself binds no ports, so a remote attacker has nothing to dial. Every hop is on the machine.
This is defence-in-depth. A drive-by page on evil.com can't reach your app's server - it would have to resolve 127.0.0.1 from the browser's origin, which the Same-Origin Policy blocks by default, and even a successful connection attempt would still face the claim-code gate below.
Gate 2 - the claim code
Section titled “Gate 2 - the claim code”Even from a permitted origin, the session is inert until claimed. The flow:
- App connects and sends
tesseron/hello. - MCP gateway generates a random 6-char code (format
XXXX-YY, excludes visually confusing characters) and returns it inwelcome. - The code is displayed out-of-band: gateway stderr, and wherever your app chooses to render it.
- The user types the code into the agent ("connect Tesseron session AB3X-7K").
- Agent calls
tesseron__claim_session. If the code matches, the session transitions toClaimedandtools/list_changedfires.
The claim code is never sent on the WebSocket from the gateway to the agent - the user carries it across. That's the whole point: it's a human-performed authorisation gesture, not an electronic one.
Strength: ~1.5 billion possible codes (6 positions × ~32 unambiguous alphanumerics), drawn uniformly from the platform CSPRNG (crypto.getRandomValues) with rejection sampling — not Math.random(), which is not cryptographically secure. A brute-force attacker would need ~750M tries for a 50% hit rate. Codes are single-use; a failed match does not retry.
Multi-app coexistence
Section titled “Multi-app coexistence”Multiple apps can be connected at once. Every MCP tool is prefixed with the app.id, so shop__addItem and admin__banUser never collide. Internally the gateway routes tools/call name=shop__addItem to the session whose app.id === "shop" - if that session has disconnected, the call errors with -32003 ActionNotFound.
This means you can, without coordinating, keep your dashboard and your customer app both connected to the same agent. Each has its own claim code, its own origin check, its own session.
What Tesseron does NOT defend against
Section titled “What Tesseron does NOT defend against”- Malicious code running in your app's process. If an attacker already executes JS in your tab or on your Node server, they can call your SDK and declare whatever actions they want. Tesseron is no worse - and no better - than the process it's embedded in.
- Malicious MCP clients on the same machine. Any local process running as the same user can read
~/.tesseron/instances/and dial one of the advertised endpoints. They would still need to send a validtesseron/helloand convince the user to type the claim code into their agent. The claim code is the second gate, and it requires human cooperation - but it's the only thing stopping a rogue process from attaching to your session. - Prompt injection. If your handler's
descriptionor inputs are attacker-controlled, they can manipulate the agent's plans. Sanitise descriptions you show to the agent the same way you would sanitise HTML you show to users. - Exfiltration via resources. Anything you expose as a resource is readable by the claimed agent. Don't expose credentials, session tokens, or PII you haven't decided the user is okay sharing with Claude.
Operational tips
Section titled “Operational tips”- Bind to
127.0.0.1, never0.0.0.0. The default in@tesseron/serverand@tesseron/viteis loopback-only; don't override it unless you know exactly why. UDS hosts go throughos.tmpdir()with a private (mode 0700) directory. ~/.tesseron/files are written private (mode 0600 inside a 0700 directory). Instance manifests and claim breadcrumbs are owner-only on POSIX. Sibling processes running as the same user can still open them — same-UID enforcement is the OS's job — but cross-user enumeration is closed. On Windows POSIX modes are advisory; the OS user model is the gate, same caveat as the UDS binding spec.- Treat the claim code as short-lived. Don't render it persistently in the UI after the session is claimed.
- Clean up instance manifests on app exit. The built-in SDKs do this for you; if you port to a new runtime, handle the shutdown path so stale files don't pile up.
- Log the
agent.idthat claimed the session - useful for auditing which agent actually ran which action. - For production tools, use per-user app IDs.
shop_kennyvsshop_sarahprevents one user's agent from driving another user's tab, even if both are on the same machine.
That's the end of the Protocol section. The SDK section picks up from here - how to speak this protocol from TypeScript today and from other languages later.