# Security Policy Arboretum is a self-hosted daemon that serves a web dashboard to drive git worktrees and the Claude Code sessions running on them. **A web terminal is remote code execution by design** — that is the product, not a bug. Arboretum's security model is therefore built on *structural* guards (loopback-only binding, authenticated access, strict Origin checks) far more than on cryptography alone. This document describes the threat model, the controls that are implemented, the deliberate trade-offs, and how to report a vulnerability. For hardening a deployment in a regulated environment, see [`docs/ENTERPRISE_DEPLOYMENT.md`](docs/ENTERPRISE_DEPLOYMENT.md). ## Threat model - **Single-user by design.** Arboretum runs on the owner's machine and is meant for one operator. There is no multi-tenant isolation and no RBAC — and none is claimed. - **Loopback by default.** The server binds `127.0.0.1`; `config.ts` *refuses* any non-loopback bind unless you pass `--i-know-this-exposes-a-terminal`. Remote access is expected via **Tailscale Serve** (TLS + tailnet identity), never by opening a port. - **The terminal is RCE.** Anyone who can authenticate can run code. The controls below exist to make sure only the authenticated operator reaches it, and that the surrounding surface (cookies, headers, data at rest) is hardened. ## Implemented controls | Area | Control | Where | | --- | --- | --- | | Network | Loopback-only default; non-loopback refused without explicit flag | `packages/server/src/config.ts` | | AuthN | Global guard on **all** `/api/**` and `/ws` (only login is public, and rate-limited) | `packages/server/src/app.ts` | | AuthN | Tokens stored **hashed** (SHA-256), compared in constant time; bootstrap token shown once | `packages/server/src/auth/service.ts` | | Sessions | Cookie is an HMAC-SHA256 signed payload, `HttpOnly` + `SameSite=Strict`, `Secure` when HTTPS | `packages/server/src/routes/auth.ts` | | CSRF / WS | Strict `Origin` check on every `/api/**` and `/ws` request (anti cross-site WS hijacking) | `packages/server/src/app.ts` | | CSRF | Mutations carrying a body must be `application/json` | `packages/server/src/app.ts` | | Rate limit | Global login rate limit with exponential backoff (not per-IP — Tailscale fronts everything as 127.0.0.1) | `packages/server/src/auth/service.ts` | | HTTP headers | CSP, `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy`, `Permissions-Policy`, conditional HSTS, `Cache-Control: no-store` on API; `Server` header stripped | `packages/server/src/app.ts` | | Injection | All SQL is parameterized; all git calls use `execFile` (no shell); path-traversal guards | `packages/server/src/**` | | Data at rest | DB file/dir forced to `0o600`/`0o700`; sensitive secrets (server secret, VAPID private key) encrypted with AES-256-GCM | `packages/server/src/db/index.ts`, `core/secret-box.ts` | | Audit | Persistent audit log of sensitive mutations (tokens, settings, secrets, push, groups) | `packages/server/src/core/audit-log.ts` | | Privacy | GDPR export (`/api/v1/data/export`) and erasure (`/api/v1/data/delete-my-data`) | `packages/server/src/routes/data.ts` | | Install | Runs as a **user** service (systemd user unit / launchd LaunchAgent), never root | `packages/server/src/cli/install.ts` | ## Deliberate trade-offs - **Long-lived API tokens.** Tokens do not expire by age (CLI automation stability) but can be revoked instantly, and `last_used_at` is tracked. Review and rotate tokens periodically. - **Encryption-at-rest key management.** With no `ARBORETUM_SECRET_KEY` set, the encryption key lives in `dataDir/secret.key` (`0o600`) next to the database — this protects a leaked database *copy* (backup, WAL) but not a full `dataDir` compromise. For strong protection, set `ARBORETUM_SECRET_KEY` and store it separately from database backups. Full-DB SQLCipher is intentionally avoided (it breaks the `npx` prebuilt portability). - **No multi-user model.** If you need multiple operators with isolation, Arboretum is not the right tool. ## Reporting a vulnerability Please report security issues **privately** — do not open a public issue. - Email: **security@johanleroy.fr** (or `contact@johanleroy.fr`). - Include a description, affected version, and reproduction steps. - Expect an acknowledgement within **7 days** and a coordinated disclosure window of up to **90 days**. Thank you for helping keep Arboretum users safe.