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