import { createFileRoute } from "@tanstack/react-router"; import { pageMeta } from "~/meta"; export const Route = createFileRoute("/docs/security")({ head: () => ({ meta: pageMeta( "Security - Paseo Docs", "Security model for Paseo: architecture overview, connection methods, relay encryption, and best practices.", ), }), component: Security, }); function Callout({ children }: { children: React.ReactNode }) { return (
Paseo follows a client-server architecture, similar to Docker. The daemon runs on your machine and manages your coding agents. Clients (the mobile app, CLI, or web interface) connect to the daemon to monitor and control those agents.
Your code never leaves your machine. Paseo is a local-first tool that connects directly to your development environment.
The Paseo daemon can run anywhere you want to execute agents: your laptop, a Mac Mini, a VPS, or a Docker container. The daemon listens for connections and manages agent lifecycles.
Clients connect to the daemon over WebSocket. There are two ways to establish this connection:
The relay is the simplest way to connect from your phone. It requires no VPN setup, no port forwarding, and no firewall configuration. The daemon can stay bound to localhost or a socket file — it connects outbound to the relay, and your phone meets it there.
$PASEO_HOME/daemon-keypair.json
The relay sees only: IP addresses, timing, message sizes, and session IDs. It cannot read message contents, forge messages, or derive encryption keys from observing the handshake.
The daemon requires a valid cryptographic handshake before processing any commands. A compromised relay cannot:
The QR code or pairing link is the trust anchor. It contains the daemon's public key, which is required to establish the encrypted connection. Treat it like a password — don't share it publicly.
If you believe a pairing offer has been compromised, restart the daemon to generate a new session ID and rotate the relay pairing.
By default, the daemon listens on 127.0.0.1:6767{" "}
(localhost only). This is safe for local CLI usage but not reachable from your phone or
other devices.
For maximum isolation, you can configure the daemon to listen on a Unix socket file instead of a TCP port. This prevents any network access entirely — only processes on the same machine can connect. The CLI supports this mode, but the mobile app and web interface require a network connection.
If you prefer direct connections over the relay, you can use a VPN like{" "} Tailscale . Tailscale creates a private network between your devices, so you can access your daemon without exposing it to the public internet.
To set this up:
100.x.y.z:6767)
allowedHosts and{" "}
cors.allowedOrigins
0.0.0.0 makes the
daemon reachable on all network interfaces, including public Wi-Fi and local networks.
This can expose your daemon to unauthorized access. If you must bind to all interfaces,
ensure you have proper firewall rules and review your{" "}
allowedHosts configuration.
CORS is not a complete security boundary. It controls which browser origins can make requests, but does not prevent a malicious website from resolving its domain to your local machine (DNS rebinding).
Paseo uses a host allowlist to validate the Host header
on incoming requests. Requests with unrecognized hosts are rejected.
Configure via daemon.allowedHosts in{" "}
config.json:
[]): allow{" "}
localhost,{" "}
*.localhost, and all IP addresses
['.example.com']: allow{" "}
example.com and any subdomain, plus defaults
true: allow any host (not recommended)
Paseo wraps agent CLIs (Claude Code, Codex, OpenCode) but does not manage their authentication. Each agent provider handles its own credentials:
~/.claude/
Paseo never stores or transmits provider API keys. Agents run in your user context with your existing credentials.