The bind-mount permission fix only takes effect after a rebuild, so a
stale image keeps failing with the opaque "unable to open database file".
This hardens both sides so any remaining failure is self-explanatory:
- docker-entrypoint.sh: fall back to chmod 777 when chown is unsupported
(network/9p mounts), and fall back to running as root when su-exec is
unavailable, so the data dir is always writable on any filesystem.
- server/db.mjs: wrap the DatabaseSync open in a try/catch and, on
failure, report the exact path, whether the directory is writable, and
the process UID instead of the bare SQLite error.
Verified with a real container: a normal bind mount serves /health and
creates trxtd.db; a read-only mount now prints the directory-permission
diagnostic instead of the raw SQLite error.
- Add CSP, frame and referrer headers for served static files
- Enforce WebSocket origin check to prevent cross-site hijacking
- Trust X-Forwarded-For only when the peer is from a private proxy network
- Limit concurrent connections (500 total / 20 per IP) and rooms (300)
- Add ALLOWED_ORIGINS env var for additional WebSocket origins
- Document reverse proxy setup (NPM/NPMplus) in README
- Add scripts/security-test.mjs to verify origin and limit behavior
- Add Dockerfile (multi-stage build, unprivileged user, healthcheck),
docker-compose.yml and .dockerignore
- Server now serves static dist/ frontend and WebSocket relay on a
single port (PORT, default 3001) with path-traversal protection,
immutable asset caching and SPA fallback
- Client connects via same origin in production; dedicated ws port is
only used for vite dev/preview
- Document Docker usage, env vars and manual production mode in README