Commits
compose.example.yaml is a copy-to-customize template and is gitignored like `.env` and `server/labelers.json` are.
The container runs hardened by default: non-root (user: node), cap_drop ALL, read-only rootfs, and no-new-privileges.
The app needs no capabilities as it uses an unprivileged port and all writes go to
/data (DB_PATH / AUDIT_LOG_PATH), which lives on a named volume.
Non-root is set via `user:` in compose rather than USER in the Dockerfile: Fly mounts volumes root-owned, so a baked-in
USER would crash existing Fly deploys on their next push. Image-only users see no behavior change from this commit.
The new `mkdir /data && chown` line in the Dockerfile makes fresh named volumes initialize writable for
the node user (docker seeds an empty named volume with the mount point's contents and ownership).
Fly's volume mount shadows the directory, so it's inert there.
The port publish is host-loopback only and a TLS-terminating reverse proxy goes in front for prod deployments.
The commented bind mounts carry :Z for selinux hosts (podman on Fedora et al.) and is a no-op on other platforms.
Passkey enrollment inside the container is `node dist/cli.js enroll`.
npm run enroll needs tsx, which is pruned with the dev deps.
compose.example.yaml is a copy-to-customize template and is gitignored like `.env` and `server/labelers.json` are.
The container runs hardened by default: non-root (user: node), cap_drop ALL, read-only rootfs, and no-new-privileges.
The app needs no capabilities as it uses an unprivileged port and all writes go to
/data (DB_PATH / AUDIT_LOG_PATH), which lives on a named volume.
Non-root is set via `user:` in compose rather than USER in the Dockerfile: Fly mounts volumes root-owned, so a baked-in
USER would crash existing Fly deploys on their next push. Image-only users see no behavior change from this commit.
The new `mkdir /data && chown` line in the Dockerfile makes fresh named volumes initialize writable for
the node user (docker seeds an empty named volume with the mount point's contents and ownership).
Fly's volume mount shadows the directory, so it's inert there.
The port publish is host-loopback only and a TLS-terminating reverse proxy goes in front for prod deployments.
The commented bind mounts carry :Z for selinux hosts (podman on Fedora et al.) and is a no-op on other platforms.
Passkey enrollment inside the container is `node dist/cli.js enroll`.
npm run enroll needs tsx, which is pruned with the dev deps.