Skip to content

Pre-v1 hardening: admin bootstrap, CSP + security headers, log stream, onboarding tour - #57

Merged
mondial7 merged 1 commit into
mainfrom
feat/pre-v1-hardening
Jul 11, 2026
Merged

Pre-v1 hardening: admin bootstrap, CSP + security headers, log stream, onboarding tour#57
mondial7 merged 1 commit into
mainfrom
feat/pre-v1-hardening

Conversation

@mondial7

Copy link
Copy Markdown
Owner

The last round of work before tagging v1.0.0. Closes #27, #32, #34 and adds a first-login onboarding tour.

#27 — Deterministic admin bootstrap (ADMIN_EMAIL)

Replaces "first user to sign in becomes admin" (a race on an empty DB) with a deterministic rule: the user whose email matches ADMIN_EMAIL is — and stays — the global admin (self-healing on login). Combined with the existing last-admin-demotion guard, admin state is now race-free and deterministic.

#32 — Security headers + CSP

  • Strict Content-Security-Policy (script-src 'self' — all JS is self-hosted, no inline scripts), plus X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy on every response.
  • Removed the one inline <script> (the SSE consolidation redirect now runs from a self-hosted app.js).
  • (Per-IP rate limiting landed earlier.)

#34 — Structured logging + admin Log Stream

  • slog with LOG_FORMAT=text|json, tee'd to stderr and an in-memory ring-buffer hub.
  • Admin Logs page streams the log live in the browser over SSE (session + stream-token guarded); LOG_FORMAT=json is the path for external shippers.

Onboarding tour

A skippable, full-screen first-login tour (5 slides → positive welcome). Tracked via a new users.onboarded_at column (migration 0002); CSP-safe (slide nav in app.js, dismiss via htmx).

Security pass

  • Real fixes: slugified the PDF filename to [a-z0-9-] (closes a Content-Disposition header-injection vector), and added a same-origin guard to all redirects.
  • govulncheck0 vulnerabilities; gosec0 issues (reviewed false positives annotated with #nosec + justification).

Verification

go build, go vet, gofmt, go test ./... (incl. Postgres gateway tests) all green. Docker release image builds, runs migrations (incl. 0002), and serves with the CSP header + JSON logs. New behaviors covered by tests (ADMIN_EMAIL bootstrap + self-heal, onboarding show/complete, log hub, SSE token gate).

Also: closed #36 (distribution project, no automated deploy) and #38 (multi-tenancy: future, not current roadmap).

…ng tour

Closes #27, #32, #34; adds a first-login onboarding tour.

- #27 Admin bootstrap via ADMIN_EMAIL: the user with this email is (and stays)
  the global admin — deterministic, replacing the first-user-becomes-admin race.
  Self-healing (promotes on login) and last-admin demotion is already guarded.
- #32 Security hardening: strict Content-Security-Policy + X-Frame-Options,
  X-Content-Type-Options, Referrer-Policy, Permissions-Policy on every response.
  Removed the one inline <script> (SSE redirect now via self-hosted app.js), so
  script-src can be 'self'. Rate limiting was already added.
- #34 Structured logging + Log Stream: slog with LOG_FORMAT=text|json tee'd to
  stderr and an in-memory hub; admin Logs page streams the log live over SSE
  (token-guarded) and documents the json-to-shipper path.
- Onboarding: a skippable, full-screen first-login tour (tracked via
  users.onboarded_at; CSP-safe — nav in app.js, dismiss via htmx).

Security pass: fixed a Content-Disposition header-injection vector (the PDF
filename is now slugified to [a-z0-9-]) and added a same-origin guard to all
redirects. govulncheck and gosec both run clean (reviewed false positives are
annotated with #nosec + justification).
@mondial7
mondial7 merged commit f65858c into main Jul 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant