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

63 lines
4.7 KiB
Markdown

## 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).