You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Support optional encrypted-at-rest secrets via SOPS
Adds opt-in SOPS support so deployers who want it can encrypt true
secrets (OPENAI_API_KEY, ODYSSEUS_ADMIN_PASSWORD, SEARXNG_SECRET, MCP
OAuth secrets, IMAP passwords, etc.) at rest instead of leaving them
plaintext in .env. Non-secret config (APP_PORT, LLM_HOST, etc.) stays
in .env so PR diffs remain reviewable.
The feature is only active when /app/secrets.env.enc is present at
container start; existing deployments behave exactly as before.
- Dockerfile: pin sops v3.13.1 (~10MB static Go binary), arch-aware
- docker/entrypoint.sh: wrap gosu with 'sops exec-env' when the
encrypted file exists; fail loudly if sops is missing or no
SOPS_AGE_KEY[_FILE] is set
- .sops.yaml: creation rule with placeholder age public key
- secrets.env.example: documents which env vars are expected as secrets
- .gitignore: ignore plaintext secrets.env, allow .example and .enc
- SECURITY.md: new 'Encrypting Secrets At Rest' section with the
age-keygen / sops -e / compose-mount workflow
Proposed in #233. Naming chosen to avoid colliding with the existing
runtime Bitwarden integration at routes/vault_routes.py.
Copy file name to clipboardExpand all lines: SECURITY.md
+50Lines changed: 50 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,6 +19,56 @@ Security fixes are handled on the default branch until formal releases are cut.
19
19
- Rotate API keys, webhook secrets, and Odysseus API tokens if they appear in logs, screenshots, demos, or shared chats.
20
20
- Treat shell, model-serving, MCP, email, calendar, and vault features as privileged admin functionality.
21
21
22
+
## Encrypting Secrets At Rest (Optional)
23
+
24
+
By default, secret values (API keys, admin password seed, SearXNG signing key, etc.) live in `.env` as plaintext. The file is gitignored, but any process that reads it (container exec, host backup, snapshot) reveals every secret. For deployments where that's a concern, Odysseus supports [SOPS](https://github.com/getsops/sops) with age keys to encrypt secrets at rest.
25
+
26
+
This is **opt-in** — the feature is only active when `secrets.env.enc` is present at container start. Existing deployments behave exactly as before.
27
+
28
+
### Setup
29
+
30
+
1. Install [`age`](https://github.com/FiloSottile/age) on your host and generate a key:
31
+
32
+
```bash
33
+
mkdir -p ~/.config/sops/age
34
+
age-keygen -o ~/.config/sops/age/keys.txt
35
+
age-keygen -y <~/.config/sops/age/keys.txt # prints your age public key
36
+
```
37
+
38
+
2. Paste the printed public key into `.sops.yaml` (replace the `age1REPLACE...` placeholder).
39
+
40
+
3. Copy the example, fill in real secret values, then encrypt:
41
+
42
+
```bash
43
+
cp secrets.env.example secrets.env
44
+
$EDITOR secrets.env
45
+
sops -e secrets.env > secrets.env.enc
46
+
shred -u secrets.env # destroy plaintext; keep only secrets.env.enc
47
+
```
48
+
49
+
4. Provide the age **private** key to the container by mounting it and pointing `SOPS_AGE_KEY_FILE` at it (`docker-compose.yml`):
0 commit comments