# Agent Name: Agent 07 - Security And Deployment Hardening Stage: Security And Deployment Hardening Date: 2026-04-15 ## Scope Hardened the existing Go/chi/SQLite application for internet-facing deployment without changing the product model or admin/client flow. Completed in this stage: - added stricter HTTP server defaults: - `ReadHeaderTimeout` - `MaxHeaderBytes` - higher default read/write/idle timeouts suitable for uploads and authenticated downloads - added global security headers and route-specific cache controls: - `Content-Security-Policy` - `X-Frame-Options` - `X-Content-Type-Options` - `Referrer-Policy` - `Permissions-Policy` - `Cross-Origin-Opener-Policy` - `Cross-Origin-Resource-Policy` - `Strict-Transport-Security` when `APP_BASE_URL` is `https` - `Cache-Control: no-store` on admin and protected client API responses - `Vary: Cookie` or `Vary: Authorization` on the appropriate routes - added trusted-proxy awareness so forwarded headers are only trusted when `TRUST_PROXY_HEADERS=true`; - added in-memory rate limiting for: - `POST /admin/login` - protected client API routes under `/api/v1` - added CSRF protection for all admin POST flows, including login, logout, project/tag/API key forms, tag attach or detach, archive or restore, and multipart release upload; - rotated or cleared the admin CSRF cookie on login and logout, and scoped both session and CSRF cookies to `/admin`; - tightened local data directory permissions for SQLite and data storage; - added regression coverage for CSRF rejection, hardened cookie defaults, security headers, and both rate-limited route groups; - added first-pass deployment assets: - multi-stage `Dockerfile` - hardened `docker-compose.yml` - Caddy example - Nginx example - env example - deployment, Proxmox, backup, and restore notes. ## Files Changed - `/Users/delete/projects/update_server/internal/config/config.go` - `/Users/delete/projects/update_server/internal/app/app.go` - `/Users/delete/projects/update_server/internal/db/open.go` - `/Users/delete/projects/update_server/internal/auth/service.go` - `/Users/delete/projects/update_server/internal/http/router.go` - `/Users/delete/projects/update_server/internal/http/security.go` - `/Users/delete/projects/update_server/internal/http/rate_limit.go` - `/Users/delete/projects/update_server/internal/http/csrf.go` - `/Users/delete/projects/update_server/internal/http/auth_handlers.go` - `/Users/delete/projects/update_server/internal/http/admin_common.go` - `/Users/delete/projects/update_server/internal/http/admin_api_keys.go` - `/Users/delete/projects/update_server/internal/http/view_data.go` - `/Users/delete/projects/update_server/internal/http/auth_integration_test.go` - `/Users/delete/projects/update_server/internal/http/projects_integration_test.go` - `/Users/delete/projects/update_server/internal/http/api_keys_integration_test.go` - `/Users/delete/projects/update_server/internal/http/security_integration_test.go` - `/Users/delete/projects/update_server/web/templates/layouts/base.gohtml` - `/Users/delete/projects/update_server/web/templates/pages/login.gohtml` - `/Users/delete/projects/update_server/web/templates/pages/project_form.gohtml` - `/Users/delete/projects/update_server/web/templates/pages/project_detail.gohtml` - `/Users/delete/projects/update_server/web/templates/pages/tag_form.gohtml` - `/Users/delete/projects/update_server/web/templates/pages/api_key_form.gohtml` - `/Users/delete/projects/update_server/web/templates/partials/csrf_field.gohtml` - `/Users/delete/projects/update_server/.dockerignore` - `/Users/delete/projects/update_server/Dockerfile` - `/Users/delete/projects/update_server/docker-compose.yml` - `/Users/delete/projects/update_server/deploy/Caddyfile.example` - `/Users/delete/projects/update_server/deploy/nginx.update-server.conf.example` - `/Users/delete/projects/update_server/deploy/update-server.env.example` - `/Users/delete/projects/update_server/docs/DEPLOYMENT.md` - `/Users/delete/projects/update_server/docs/agents/handoffs/07-security-deploy.md` ## Database Changes - no migrations or schema changes were required; - SQLite remains the production database; - deployment notes now explicitly call out WAL-mode backup and restore handling for: - `db.sqlite` - `db.sqlite-wal` - `db.sqlite-shm` - `artifacts/`. ## API Or Route Changes - no new product endpoints were added; - middleware behavior changed for existing routes: - all `/admin` responses now emit hardened headers and `no-store` caching rules; - all `/admin` POST routes now require a valid CSRF token; - `POST /admin/login` is rate limited by client IP; - protected `/api/v1` routes now emit hardened headers and `no-store` caching rules; - protected `/api/v1` routes are rate limited by client IP before API-key auth; - session cookies are now scoped to `/admin` and retain `HttpOnly`, `SameSite=Lax`, and conditional `Secure`; - CSRF cookies are now scoped to `/admin` and use `HttpOnly`, `SameSite=Strict`, and conditional `Secure`. ## Commands And Tests Run - `gofmt -w internal/config/config.go internal/app/app.go internal/db/open.go internal/auth/service.go internal/http/router.go internal/http/security.go internal/http/rate_limit.go internal/http/csrf.go internal/http/auth_handlers.go internal/http/view_data.go internal/http/admin_common.go internal/http/admin_api_keys.go internal/http/auth_integration_test.go internal/http/projects_integration_test.go internal/http/api_keys_integration_test.go internal/http/security_integration_test.go` - passed - `go test ./internal/http` - passed - `go test ./...` - passed - `go build ./cmd/server` - passed - `go build ./cmd/migrate` - passed - `docker compose config` - could not run in this environment because `docker` is not installed ## Known Limitations - rate limiting is in-memory and per-process, so counters reset on restart and are not shared across multiple app instances; - Docker and reverse-proxy examples were added and reviewed statically, but they were not live-validated here because Docker is unavailable in this workspace; - `Strict-Transport-Security` only appears when `APP_BASE_URL` is configured with `https`, which is the intended production setup behind TLS termination; - the proxy still must be configured correctly to keep the app private and to strip or control forwarded headers before `TRUST_PROXY_HEADERS=true` is safe. ## Recommended Next Step Run the Agent 07 validation pass, with special attention to: - CSRF enforcement on every admin POST flow, including multipart upload; - security headers and private-cache headers on admin and protected client API responses; - login and client API rate limiting behavior; - cookie flags and `/admin` cookie scoping; - deployment docs and example proxy/container files on a host that has Docker and a reverse proxy available. ## Notes For Validator - verify that a missing or invalid admin CSRF token returns `403` and that the normal admin flows still succeed when the token is present; - verify that session cookies are only scoped to `/admin` and that secure-cookie behavior follows the configured `APP_BASE_URL` scheme; - verify that `TRUST_PROXY_HEADERS=false` leaves client IP handling on the socket remote address; - verify that protected `/api/v1` responses include `Cache-Control: no-store`, `Vary: Authorization`, and the shared security headers; - if possible, validate `docker compose up --build` plus one reverse-proxy example on a real machine, since that could not be executed in this environment.