Skip to content

ADR-0007 · Plaintext Secrets + OS Hardening

The chosen approach is to store API keys and tokens as plaintext in ~/.hermes/.env on the Agent Host VPS, hardened at the OS level, rather than using a secrets manager or encrypted-at-rest solution.

The Hermes Agent needs runtime access to multiple secrets: Slack bot token, OpenRouter API key, Francois Personal Google Drive OAuth2 refresh token, GitHub fine-grained PATs scoped to specific fducat18 repos, and potentially more. These are stored in ~/.hermes/.env — a flat file that Hermes reads at startup. The day-one dashboard must not display or edit .env values. Djuly Google Drive, professional Google Drive, and professional GitHub tokens are explicitly out of day-one Agent Host scope.

Plaintext .env with OS-level hardening (day one). Upgrade path documented below.

Use separate fine-grained personal GitHub PATs:

Token Scope Permissions Purpose
HERMES_GITHUB_CODE_PAT Selected personal fducat18 repos only contents:write, pull_requests:write Let Hermes edit personal code/wiki repos through branch-review delivery
HERMES_BACKUP_GITHUB_PAT Private hermes-backups repo only contents:write Push daily encrypted Hermes backup archives

Avoid classic PATs, wildcard repo scope, professional org access, and long-lived tokens when a 90-day expiry is tolerable.

  1. Dedicated user — Hermes runs as hermes user (not root). The .env file is owned by hermes:hermes with chmod 600.
  2. SSH-key-only access — password and root login disabled on VPS. Only Francois’s ed25519 key can connect.
  3. UFW firewall — only port 22 (SSH) open inbound. All other traffic flows through Cloudflare Tunnel (outbound-only from VPS). Keeping SSH on 22 is acceptable because security comes from key-only auth, fail2ban, updates, and firewalling, not port obscurity.
  4. Dashboard binds to localhost — port 9119 listens on 127.0.0.1 only. External access is via Cloudflare Tunnel + Access (email OTP).
  5. No web secret editing day one — dashboard is read/operate only (sessions, logs, health, chat, non-secret operations). .env display/edit is done only over SSH from Bitwarden.
  6. No secrets in git — the .env file is never committed. The repo contains only .env.example with placeholder values.
  7. Slack webhook via tunnel — not a public HTTP endpoint; delivered through the authenticated tunnel.
Threat Impact Mitigation Residual risk
VPS root compromise All secrets exposed SSH-key-only, UFW, unattended-upgrades, fail2ban Low — attacker needs the private key
Cloudflare Tunnel breach Dashboard access to sessions, logs, health, chat, and non-secret operations CF Access email OTP + dashboard on localhost only + no web secret editing day one Low — requires compromising Cloudflare AND the owner’s email
Backup leak (VPS snapshot with .env) Secrets in backup Exclude ~/.hermes/.env from automated backups; re-provision secrets from Bitwarden if needed Low — manual backup process
Log/stdout leak Token appears in error log Hermes redacts secrets in logs by default Very low
Hostinger employee access Theoretical hypervisor access Accept provider trust (same as any VPS/cloud) Accepted risk
  • If someone has root, they can read the decryption key (it must be available at boot for the service to start)
  • Adds complexity (systemd ExecStartPre decrypt step, key management)
  • The real protection is preventing unauthorized access (SSH keys, firewall, tunnel), not encrypting something that must be decrypted to be used
Trigger Upgrade to
Add Gmail/email access (highly sensitive) Bitwarden Secrets Manager ($6/mo) — secrets fetched at runtime, never on disk
Add family members with VPS access Per-user secret scoping + encrypted at rest with age
Regulatory requirement (client data) Hashicorp Vault or Infisical (full audit trail, rotation, leases)
  • No encryption at rest — if someone images the VPS disk, secrets are readable
  • No audit trail — no log of “who accessed which secret when” (Bitwarden Secrets Manager would provide this)
  • Manual rotation — changing a key means editing .env and restarting the service

These tradeoffs are acceptable for a single-user personal estate with no client data and strong perimeter security.