Open source · Personal project
← Selected workMinnumst: a self-hosted family history archive
Minnumst (Icelandic: "let us remember") is a shared written history of our ancestors: a public archive for reading stories, an editorial desk for writing them, and an API that keeps every story revision. I built it end to end, from the app to the hardened host it runs on, so that the only thing reachable from the internet is a TLS-terminating proxy and every write is authenticated before it reaches the application.

- 57 → 75
- Lynis hardening index, scored in CI
- 254 → 0
- Trivy findings, API image moved to Alpine
- 1
- public entry point: Traefik
Network and access
- Only Traefik publishes ports. HTTP redirects to HTTPS with Let's Encrypt certificates and HSTS, and the API is rate-limited.
- Authentik forward-auth, with TOTP MFA, guards the whole editorial desk and every write to the API; reads stay public and there is no in-app login to attack.
- PostgreSQL sits alone with the API on an internal network with no route in or out. Traefik discovers services through a socket proxy that only lists containers.
Host, provisioned as code
- OpenTofu creates the Hetzner server, firewall and data volume, with state encrypted before it leaves the operator's machine.
- Ansible hardens Ubuntu 24.04 to the CIS benchmark (Level 1, selected Level 2), with every setting citing its CIS section and NIST control: LUKS-encrypted data, unattended updates, key-only SSH, a default-deny nftables firewall and auditd.
- Nightly database backups are encrypted with age to a key that never touches the server, and CI restores one into a fresh database on every change.
Containers and supply chain
- Every container runs as non-root, with a read-only root filesystem, no Linux capabilities and no-new-privileges, on Alpine images pinned by digest.
- Dependencies are locked, every GitHub Action is pinned to a commit SHA, and CodeQL, Trivy and zizmor scan the code, images and workflow on every change.
- Releases are multi-arch images published with build provenance and an SBOM.
Verified, not just configured
- A CI job applies the host hardening to a real Ubuntu 24.04 VM, requires a second run to change nothing, and reports the Lynis index before and after.
- An end-to-end job runs the production Compose stack and checks the edge from outside: redirect, HSTS and CSP, refused unauthenticated writes, the rate limit and network isolation.
- Playwright browser tests of both frontends fail on any Content Security Policy violation.
Technologies
- OpenTofu
- Ansible
- Docker Compose
- Traefik
- Authentik
- FastAPI
- React
- PostgreSQL