# systemd unit for the FastAPI process. # # Install: # sudo cp deploy/systemd/rfc-app.service /etc/systemd/system/ # sudo systemctl daemon-reload # sudo systemctl enable --now rfc-app # sudo systemctl status rfc-app # # Logs: # sudo journalctl -u rfc-app -f # # Per §4.2 the app is intentionally single-process — one uvicorn # worker, colocated SQLite. If the deployment ever needs more than one # worker, the spec calls for a planned migration to Postgres first; # raising `--workers` here would break the WAL-mode SQLite invariant. [Unit] Description=Wiggleverse RFC app After=network.target Documentation=https://git.wiggleverse.org/ben.stull/rfc-app [Service] Type=simple User=rfc-app Group=rfc-app WorkingDirectory=/opt/rfc-app/backend EnvironmentFile=/opt/rfc-app/backend/.env ExecStart=/opt/rfc-app/backend/.venv/bin/uvicorn app.main:app \ --host 127.0.0.1 \ --port 8000 \ --proxy-headers \ --forwarded-allow-ips 127.0.0.1 Restart=on-failure RestartSec=5s # Hardening — modest defaults; tighten further if the host runs other # services. The bot wrapper writes only to its own data dir; everything # else is read-only. NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true ReadWritePaths=/opt/rfc-app/backend/data # v0.25.0 security hardening (audit 0026 L4) — defense-in-depth. # The service binds 127.0.0.1:8000 and runs plain CPython # (FastAPI/uvicorn + sqlite + bcrypt + httpx), so it needs no # capabilities and no exotic syscalls. CapabilityBoundingSet= AmbientCapabilities= PrivateDevices=true ProtectKernelTunables=true ProtectKernelModules=true ProtectKernelLogs=true ProtectControlGroups=true RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX RestrictNamespaces=true LockPersonality=true # MemoryDenyWriteExecute=true blocks W^X memory — safe for stock # CPython (no JIT) and the pure-Python/C-extension stack here, but # would break a JIT or a C-ext that mmaps W+X. Watch the first # restart's journal for a crash; if uvicorn fails to come up, # comment this one line out and reload. MemoryDenyWriteExecute=true RestrictRealtime=true RestrictSUIDSGID=true SystemCallFilter=@system-service SystemCallErrorNumber=EPERM SystemCallArchitectures=native UMask=0077 [Install] WantedBy=multi-user.target