Operations
Security model
Phase 1 assumes a single trusted user controls the VPS. We've made the defaults safe for that scenario; some of them get more sophisticated in Phase 2 (multi-user, SSO).
Auth
- Password: argon2id (memCost 19_456, t=2, p=1, OWASP defaults), min 12 chars
- Session token: 32 random bytes (hex), stored as SHA-256 hash in SQLite
- Cookie:
HttpOnly,Secure,SameSite=Strict, 30-day expiry, refreshed
on use
- Rate limit: 5 attempts per 15 minutes per IP on
/api/auth/login - Log out everywhere: nukes all session rows for the user
At-rest encryption
Per-provider AI API keys are encrypted before being written to SQLite:
- Algorithm: AES-256-GCM
- Key: 32 random bytes in
~/.sudoterm/master.key(chmod 600, created on
first run)
- Output format: base64(iv ‖ ciphertext ‖ authTag)
- Decryption: only in memory, during a streaming request
Network
- The daemon binds to
127.0.0.1:7681by default — no exposure to the LAN - Public access is through Cloudflare Tunnel (
cloudflared), which establishes
an outbound HTTPS connection — no inbound ports required
- TLS is terminated at the Cloudflare edge with a Let's Encrypt cert provisioned
by Cloudflare for *.sudoterm.com
- WebSocket upgrades flow over the same tunnel
WebSocket auth
Cookies don't cross ports in the browser. The daemon mints a one-time WS token (POST /api/auth/ws-token, 60s TTL) on demand — that's what the browser attaches as ?token=… to its terminal WS URL.
What we don't do (Phase 1)
- Multi-user, RBAC, SSO — single user only
- MFA — defer to your password manager
- Audit log — Phase 3 (multi-tenant)
- E2E encryption between browser and daemon — TLS to Cloudflare is good enough
for the use case