Security & Threat Model
Gatehouse is designed for the homelab threat model: a single operator, one or more AI agents with varying degrees of trust, and a small set of credentials worth protecting. This page documents what the system protects against and the assumptions it relies on.
What Gatehouse protects#
- Credential leaks from agent context windows. Proxy mode keeps the key server-side. The agent only sees the API response.
- Secret sprawl. All credentials live in one place with a single audit log.
- Static credential exposure. Dynamic secrets eliminate long-lived database and SSH credentials.
- Privileged token misuse. Per-agent AppRoles with scoped policies replace shared tokens.
- Accidental logging of credentials. The scrubber catches 15+ common credential formats in MCP and REST responses.
- Encryption at rest. Every secret is encrypted with a per-secret DEK, which is encrypted with a KEK derived from the master key via HKDF-SHA256.
What Gatehouse does not protect against#
- Host compromise. If an attacker gets root on the container host, they can read the master key from process memory,
/proc/$PID/environ, or viadocker inspectif the master key is set as an env var. For higher-trust deployments, pass the master key via a Docker secrets mount instead of an env var. - A malicious admin. An admin user with the
admincapability can read any secret and revoke any lease. The audit log records what they did, but it does not prevent them. - Side channels in upstream APIs. If an agent proxies a request and the upstream API includes the credential in its response (a misconfiguration on the upstream’s side), Gatehouse cannot un-leak it.
Deployment recommendations#
Master key handling#
The GATEHOUSE_MASTER_KEY is never written to disk by Gatehouse. Losing it means losing access to every encrypted secret. Store the master key:
- For homelabs: in a password manager separate from the one that holds Gatehouse credentials, plus a printed backup in a safe place.
- For higher-trust deployments: use Docker secrets (mounted as a file at
/run/secrets/master_key) instead of an env var, so the key does not appear indocker inspectoutput or/proc/$PID/environ.
Root token handling#
The root token is for bootstrapping only. Create a real admin user via the web UI, log in as that user, verify everything works, then:
- Stop the container.
- Remove the
GATEHOUSE_ROOT_TOKENenv var from yourdocker-compose.ymlordocker runcommand. - Restart the container.
The root token still works for bootstrapping if you set it again later, but leaving it set in production bypasses every access control check.
Policy discipline#
Give each agent its own AppRole with the minimum capabilities it needs. An agent that only reads from api-keys/* should not have delete or admin. The web UI makes it easy to inspect which policies each AppRole references and what paths each policy covers.
Network exposure#
Gatehouse listens on port 3100 by default. For homelab use, keep it on a private network. If you must expose it to the internet, put it behind a reverse proxy with TLS (Caddy, nginx, Traefik) and consider IP allowlisting on the proxy.
Cryptographic details#
- Symmetric encryption: XSalsa20-Poly1305 via
tweetnacl. Per-secret random 32-byte DEK, encrypted with a KEK derived from the master key via HKDF-SHA256 with a domain separation label. - Password hashing: Argon2id via
Bun.password.hashwith default parameters (64 MiB memory, 3 iterations, parallelism 1). - JWT signing: HS256 with the
GATEHOUSE_JWT_SECRETenv var (generated at first startup if unset and persisted to the config directory). - TOTP: RFC 6238, SHA1, 6 digits, 30-second period.
- Recovery codes: 10 one-time codes per user, each 8 characters, hashed with Argon2id before storage.