Plan: Security Hardening & Documentation — COMPLETE
Prompted by user feedback — reluctance to run a public-facing server with write access without understanding the threat model.
Problem
There's no security documentation. Security-conscious users have to guess at the attack surface and hardening options. This blocks public adoption.
What Was Done
1. Security page on the website — DONE
docs/site/security/index.md+ pages branchsecurity.md- Attack surface, token compromise, process compromise analysis
- Comparison table (SSH vs Web+CGI vs Gemini vs Demarkus)
- Systemd hardening guide with verification command
2. Systemd hardening in install script + deployment docs — DONE
setup_systemdgenerates unit with ProtectSystem=strict, ReadWritePaths, NoNewPrivileges, etc.- Conditional ProtectHome (omitted when content root is under /home)
- readlink -f to canonicalize content root before writing unit
- Deployment docs updated with hardened systemd example
3. Install script detects insecure existing config on update — DONE
_do_update_innerchecks for missing ProtectSystem in existing unit- Interactive prompt (defaults to yes, skips in non-interactive mode)
- Backs up unit before modifying, rolls back if service fails to start
- Points to public security docs URL
4. Read-only mode (-read-only flag) — DONE
DEMARKUS_READ_ONLYenv var +-read-onlyflag on server- Handler rejects PUBLISH/APPEND/ARCHIVE with
not-permittedstatus - Config accepts
1,true,yesas truthy values - Tests for handler rejection and config parsing
5. demarkus-publish CLI tool — DONE
server/cmd/demarkus-publish/main.go- Writes directly to versioned store on disk (same
store.Write()as server) - Supports
-bodyflag or stdin input - Detects duplicate content (no-op on unchanged)
- Enables local publishing when server runs read-only
6. Read-only chroot install script — DONE
install-readonly.sh— separate script for maximum security deployments- Chroot structure:
/srv/demarkus/{bin,content,tls} - Systemd unit with RootDirectory, ReadOnlyPaths=/, BindReadOnlyPaths=/dev/urandom
- Installs
demarkus-publishto /usr/local/bin for local publishing - SHA-256 checksum verification on downloads
- Rejects dangerous root paths (/, /usr, /etc, etc.)
- No auto firewall changes (user decides)
7. Cleanup
- Removed redundant
demarkus.servicefrom repo root - Updated docs: reference (config table), server (read-only section), install (binary table, readonly option), deployment (security link)
Key Design Decisions
- Shared store code:
demarkus-publishcallsstore.Write()directly — same code path as the server. No duplication. not-permittednotunauthorized: Read-only rejection is a server policy, not an auth failure.- Separate install script: Read-only chroot install is a different deployment model, kept separate to avoid complicating the main install script.
- No iptables: Replaced fragile iptables write isolation with read-only mode + chroot, which is simpler and more secure.