Files
where_woof/openspec/changes/frontend-foundation/design.md

4.7 KiB

Context

WhereWoof (return-tag platform, see where_woof.md) has no code yet. This change builds the GOAT front-end foundation: a Go application serving the owner experience (auth, tag setup/management) and the public tag page, against a single shared Postgres database. The Laravel admin, scan flow (geolocation/SMS), billing, and deployment are later phases — this change deliberately excludes them.

Goals / Non-Goals

Goals:

  • A working Go app (frontend/) with register/login/logout, My Tags CRUD, tag setup, and the public /t/:tag_code page.
  • Postgres schema (db/schema.sql) matching the settled data model (users, tags, scans).
  • The GOAT experience: server-rendered templates + HTMX partial swaps + Alpine for small client behaviour + Tailwind styling.
  • Clean seams so Phase 2 (scan flow) and Phase 4 (Laravel) can build on the same schema.

Non-Goals:

  • Scan flow (geolocation, SMS alerts, tel:/sms: links) — Phase 2.
  • Photo uploads (URL field only) — Phase 6.
  • Laravel admin, billing, plans, scan-limit enforcement — later phases.
  • Deployment/rsync/Caddy — Phase 7.
  • Production tag provisioning UI (tags are pre-coded at manufacture; admin seeds them).

Decisions

  1. Go stdlib net/http with Go 1.22+ ServeMux — no web framework. The enhanced mux supports method + wildcard patterns (GET /t/{tag_code}). Rationale: zero dependencies, the plan explicitly says "no framework", and it keeps the skills-update surface small. Alternative considered: chi — rejected as unnecessary for ~10 routes.

  2. Postgres access via pgx (pool) + sqlc (generated queries). sqlc generates type-safe query code from SQL files, so schema and queries stay in sync and compile-checked. Rationale: settled decision; sqlc is also a good skills-update item. Alternative: raw database/sql — more boilerplate, no compile-time safety.

  3. Sessions: signed cookie sessions (gorilla/sessions, cookie store). Cookie holds the user id, signed + encrypted with a server secret from env. Rationale: no session table needed in Phase 1, stateless, simple. Alternative: DB-backed sessions — better revocation control, but an extra table and lookup; revisit under anti-abuse (Phase 9).

  4. Passwords: bcrypt (golang.org/x/crypto/bcrypt). Standard, no discussion.

  5. Tag code: opaque random string (URL-safe, ~10 chars), stored as TEXT, unique. Printed on QR + NFC at manufacture. Phase 1 uses a seed script with test codes (e.g. TEST000001); the public tag-creation UI belongs to the Laravel admin later. No public endpoint creates tags — that's the anti-abuse posture from day one.

  6. Schema ownership: db/schema.sql is canonical until Laravel exists. Idempotent (CREATE TABLE IF NOT EXISTS) and applied via a make db-up target against DATABASE_URL. Laravel takes over migration ownership in Phase 4 (settled decision), generating migrations from this file.

  7. Templates: html/template with a base layout + per-page templates; HTMX partials for the account list/edit flows; Alpine for the tag page's small behaviours; Tailwind via CDN. Rationale: matches the plan's UI sketch philosophy and keeps Phase 1 dependency-light. CDN Tailwind is fine now; a build step can replace it under Phase 3 polish.

Risks / Trade-offs

  • [Cookie-session tampering/revocation limits] → Secret from env, HttpOnly + SameSite=Lax; DB-backed sessions listed under Phase 9 hardening.
  • [Tag-code enumeration — public page accepts any code] → Codes are long random strings; unknown codes return a generic "not found" page; rate limiting deferred to anti-abuse phase.
  • [sqlc codegen adds a build step] → make generate target; queries kept in one queries.sql.
  • [No framework = more wiring (routing, parsing, validation)] → Small surface (~12 routes, 4 forms); helpers in handlers/.
  • [Tailwind CDN in production is a network dependency] → Accepted for Phase 1; replace with a compiled build under Phase 3.
  • [Schema drift if Laravel later re-migrates] → Phase 4 generates Laravel migrations from db/schema.sql; drift caught by matching table definitions.

Migration Plan

  1. Apply db/schema.sql to a dev Postgres (make db-up); create the DB if missing.
  2. Seed test tag codes (make seed) for manual verification.
  3. Run the app (DATABASE_URL=... go run .), verify register → add tag → edit details → public page.
  4. No rollback path needed beyond dropping the dev DB; production rollout is Phase 7.

Open Questions

  • Exact tag code format/length (manufacturer decision) — placeholder: 10-char URL-safe base32.
  • Production Postgres hosting (likely a Docker PG on .13) — Phase 7.
  • Session store choice revisited under anti-abuse hardening (Phase 9).