Tech

Bernato security model

Akshay Sarode

Identity

The daemon accepts two auth paths:

Legacy static machine secrets from old ~/.bernato_auth.json files are cleared on load and no longer authenticate.

Pairing: cryptographically-random tokens minted by the tray, single-use, 180-second TTL, constant-time compared, and carried in the URL fragment so they do not leak into access logs. Discussed in detail in Pair a machine in 30 seconds without SSH keys.

Network

Daemon binds to 127.0.0.1:33120 exclusively — never 0.0.0.0. Off-LAN traffic flows through a single outbound wss:// connection to the Bernato broker — no inbound ports, no third-party tunnel binaries. All API requests, SSE, and terminal sessions are multiplexed over that one socket; TLS terminates at the broker edge.

Broker admission: the daemon authenticates its outbound connection with its Firebase identity, and the daemon still enforces its own auth and capability gates on every relayed request — the broker is not trusted to pre-authorize.

Sandbox

On macOS, every spawned agent is wrapped in sandbox-exec -f <profile> with a Seatbelt profile generated from a capability token bundle. Default policy: workspace-write, network, subprocess. Broader filesystem reads require explicit read:<path> or read_anywhere grants. Per-agent grants live in versioned JSON. Linux supports opt-in bwrap when available; Windows has limited Job Object containment without filesystem/network isolation. Detailed: Sandboxing Claude Code on macOS.

Audit

Bernato records local agent history and capability decisions for later review. Hash-chained verification for privileged audit trails is planned, not shipped.

Revocation

Tray menu → Revoke all sessions calls a Cloud Function that runs admin.auth().revokeRefreshTokens(uid) against the user's Firebase account. Existing ID tokens expire within 1 hour; refresh tokens are immediately dead.

Threat model

In scope: a malicious user on the LAN trying to drive the daemon (blocked: loopback bind + auth). A broker that tries to pre-authorize traffic (blocked: the daemon re-checks auth and capabilities on every relayed request). On macOS, a prompt-injected agent trying to write outside its workspace (blocked by Seatbelt). A stolen device (mitigation: revoke-all-sessions).

Not in scope: a compromise of the user's macOS account (game over). A prompt-injected agent making bad commits within its workspace (sandbox is a containment story, not a behavior story). DNS exfiltration via permitted network capability.

Reporting issues

Email security@bernato.dev. PGP key on the page. We respond within 48h.

FAQ

Where does data live?

Metadata in Firestore (your Firebase project). PTY streams in real time, not persisted server-side. history search is local SQLite on the daemon's disk.

Can the daemon be reached over LAN from another box?

No — bound to loopback only. The 'remote daemon' use case (laptop dashboard, Pi daemon) goes via the tunnel.

Is the tunnel relay logged?

nginx access log only. No request bodies, no PTY content.