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_codepage. - 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
-
Go stdlib
net/httpwith 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. -
Postgres access via
pgx(pool) +sqlc(generated queries).sqlcgenerates 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: rawdatabase/sql— more boilerplate, no compile-time safety. -
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).
-
Passwords: bcrypt (
golang.org/x/crypto/bcrypt). Standard, no discussion. -
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. -
Schema ownership:
db/schema.sqlis canonical until Laravel exists. Idempotent (CREATE TABLE IF NOT EXISTS) and applied via amake db-uptarget againstDATABASE_URL. Laravel takes over migration ownership in Phase 4 (settled decision), generating migrations from this file. -
Templates:
html/templatewith 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 generatetarget; queries kept in onequeries.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
- Apply
db/schema.sqlto a dev Postgres (make db-up); create the DB if missing. - Seed test tag codes (
make seed) for manual verification. - Run the app (
DATABASE_URL=... go run .), verify register → add tag → edit details → public page. - 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).