docs(01-01): complete project scaffold + core infrastructure plan

- SUMMARY.md with full execution details, 2 task commits, 2 deviations
- STATE.md updated with position (Plan 2/14), decisions, metrics
- REQUIREMENTS.md: ARCH-01, ARCH-04, ARCH-05, ARCH-06, ARCH-07 marked complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikkel Georgsen 2026-03-01 03:47:04 +01:00
parent 16caa12d64
commit 8be69688e9
3 changed files with 190 additions and 17 deletions

View file

@ -18,13 +18,13 @@ Requirements for Phase 1 (Development Focus: Live Tournament Management). Each m
### Architecture & Infrastructure ### Architecture & Infrastructure
- [ ] **ARCH-01**: Leaf Node runs as single Go binary on ARM64 SBC with embedded LibSQL, NATS JetStream, and WebSocket hub - [x] **ARCH-01**: Leaf Node runs as single Go binary on ARM64 SBC with embedded LibSQL, NATS JetStream, and WebSocket hub
- [ ] **ARCH-02**: Virtual Leaf runs same Go codebase on Core cloud infrastructure for free-tier venues (requires internet) - [ ] **ARCH-02**: Virtual Leaf runs same Go codebase on Core cloud infrastructure for free-tier venues (requires internet)
- [x] **ARCH-03**: All financial values stored as int64 cents — never float64 - [x] **ARCH-03**: All financial values stored as int64 cents — never float64
- [ ] **ARCH-04**: NATS JetStream embedded on Leaf with `sync_interval: always` for durability - [x] **ARCH-04**: NATS JetStream embedded on Leaf with `sync_interval: always` for durability
- [ ] **ARCH-05**: WebSocket hub broadcasts state changes to all connected clients within 100ms - [x] **ARCH-05**: WebSocket hub broadcasts state changes to all connected clients within 100ms
- [ ] **ARCH-06**: SvelteKit frontend embedded in Go binary via `//go:embed` for single-binary deployment - [x] **ARCH-06**: SvelteKit frontend embedded in Go binary via `//go:embed` for single-binary deployment
- [ ] **ARCH-07**: Leaf is sovereign — all tournament logic runs locally, cloud is never required for operation - [x] **ARCH-07**: Leaf is sovereign — all tournament logic runs locally, cloud is never required for operation
- [x] **ARCH-08**: Append-only audit trail for every state-changing action (operator, action, target, previous/new state, timestamp) - [x] **ARCH-08**: Append-only audit trail for every state-changing action (operator, action, target, previous/new state, timestamp)
- [ ] **ARCH-09**: Automated daily backup of LibSQL database to USB or cloud, with documented recovery procedure - [ ] **ARCH-09**: Automated daily backup of LibSQL database to USB or cloud, with documented recovery procedure
- [ ] **ARCH-10**: Leaf must recover cleanly from hard power-cycle during active tournament (verified by chaos testing) - [ ] **ARCH-10**: Leaf must recover cleanly from hard power-cycle during active tournament (verified by chaos testing)
@ -274,13 +274,13 @@ Which phases cover which requirements. Updated during roadmap reorganization.
| Requirement | Phase | Status | | Requirement | Phase | Status |
|-------------|-------|--------| |-------------|-------|--------|
| ARCH-01 | Phase 1 | Pending | | ARCH-01 | Phase 1 | Complete |
| ARCH-02 | Phase 3 | Pending | | ARCH-02 | Phase 3 | Pending |
| ARCH-03 | Phase 1 | Complete | | ARCH-03 | Phase 1 | Complete |
| ARCH-04 | Phase 1 | Pending | | ARCH-04 | Phase 1 | Complete |
| ARCH-05 | Phase 1 | Pending | | ARCH-05 | Phase 1 | Complete |
| ARCH-06 | Phase 1 | Pending | | ARCH-06 | Phase 1 | Complete |
| ARCH-07 | Phase 1 | Pending | | ARCH-07 | Phase 1 | Complete |
| ARCH-08 | Phase 1 | Complete | | ARCH-08 | Phase 1 | Complete |
| ARCH-09 | Phase 7 | Pending | | ARCH-09 | Phase 7 | Pending |
| ARCH-10 | Phase 7 | Pending | | ARCH-10 | Phase 7 | Pending |

View file

@ -12,25 +12,25 @@ See: .planning/PROJECT.md (updated 2026-02-28)
Phase: 1 of 7 (Tournament Engine) Phase: 1 of 7 (Tournament Engine)
Plan: 2 of 14 in current phase Plan: 2 of 14 in current phase
Status: Executing Phase 1 Status: Executing Phase 1
Last activity: 2026-03-01 — Completed Plan B (Database Schema + Migrations) Last activity: 2026-03-01 — Completed Plan A (Project Scaffold + Core Infrastructure) and Plan B (Database Schema + Migrations)
Progress: [█░░░░░░░░░] 14% Progress: [█░░░░░░░░░] 14%
## Performance Metrics ## Performance Metrics
**Velocity:** **Velocity:**
- Total plans completed: 1 - Total plans completed: 2
- Average duration: 10min - Average duration: 12min
- Total execution time: 0.17 hours - Total execution time: 0.42 hours
**By Phase:** **By Phase:**
| Phase | Plans | Total | Avg/Plan | | Phase | Plans | Total | Avg/Plan |
|-------|-------|-------|----------| |-------|-------|-------|----------|
| 01-tournament-engine | 1 | 10min | 10min | | 01-tournament-engine | 2 | 25min | 12min |
**Recent Trend:** **Recent Trend:**
- Last 5 plans: 01-02 (10min) - Last 5 plans: 01-01 (15min), 01-02 (10min)
- Trend: starting - Trend: starting
*Updated after each plan completion* *Updated after each plan completion*
@ -47,6 +47,9 @@ Recent decisions affecting current work:
- [Init]: All monetary values int64 cents — never float64 (CI gate test required) - [Init]: All monetary values int64 cents — never float64 (CI gate test required)
- [Init]: go-libsql has no tagged releases — pin to commit hash in go.mod - [Init]: go-libsql has no tagged releases — pin to commit hash in go.mod
- [Init]: Netbird reverse proxy is beta — validate player PWA access in Phase 1 before depending on it in Phase 8 - [Init]: Netbird reverse proxy is beta — validate player PWA access in Phase 1 before depending on it in Phase 8
- [01-01]: NATS server v2.12.4 requires Go 1.24+ — auto-upgraded from Go 1.23
- [01-01]: WebSocket JWT via query parameter (browser WS API limitation)
- [01-01]: JWT signing key ephemeral per startup — will persist in auth plan
- [01-02]: go-libsql requires single-statement Exec — migration runner splits SQL files into individual statements - [01-02]: go-libsql requires single-statement Exec — migration runner splits SQL files into individual statements
- [01-02]: go-libsql PRAGMA handling is inconsistent — use QueryRow for journal_mode, execPragma helper for others - [01-02]: go-libsql PRAGMA handling is inconsistent — use QueryRow for journal_mode, execPragma helper for others
- [01-02]: Force single DB connection during migrations (SetMaxOpenConns(1)) for table visibility - [01-02]: Force single DB connection during migrations (SetMaxOpenConns(1)) for table visibility
@ -67,5 +70,5 @@ None yet.
## Session Continuity ## Session Continuity
Last session: 2026-03-01 Last session: 2026-03-01
Stopped at: Completed 01-02-PLAN.md (Database Schema + Migrations) Stopped at: Completed 01-01-PLAN.md (Project Scaffold + Core Infrastructure)
Resume file: None Resume file: None

View file

@ -0,0 +1,170 @@
---
phase: 01-tournament-engine
plan: 01
subsystem: infra
tags: [go, nats, jetstream, libsql, websocket, chi, jwt, embed, sveltekit]
# Dependency graph
requires: []
provides:
- Go binary scaffold with cmd/leaf entry point
- Embedded NATS JetStream with AUDIT and STATE streams
- LibSQL database with WAL mode and migration runner
- WebSocket hub with JWT auth and tournament-scoped broadcasting
- chi HTTP server with middleware (auth, CORS, body limits, timeouts)
- SvelteKit SPA stub served via go:embed with fallback routing
- Health endpoint reporting all subsystem status
- Integration test suite (9 tests)
affects: [01-tournament-engine]
# Tech tracking
tech-stack:
added:
- go-libsql v0.0.0-20251219133454 (pinned commit, no tagged releases)
- nats-server v2.12.4 (embedded, JetStream sync=always)
- nats.go v1.49.0 (client + jetstream package)
- coder/websocket v1.8.14
- go-chi/chi v5.2.5
- golang-jwt/jwt v5.3.1
patterns:
- Embedded NATS with DontListen=true, in-process client connection
- WebSocket JWT auth via query parameter (not header)
- Tournament-scoped broadcasting via client subscription
- UUID validation before NATS subject construction (injection prevention)
- go:embed SPA with fallback routing for client-side routing
- Reverse-order graceful shutdown on signal
key-files:
created:
- cmd/leaf/main.go
- cmd/leaf/main_test.go
- internal/nats/embedded.go
- internal/nats/publisher.go
- internal/server/server.go
- internal/server/ws/hub.go
- internal/server/middleware/auth.go
- internal/server/middleware/role.go
- internal/server/middleware/bodylimit.go
- internal/store/db.go
- internal/store/migrate.go
- frontend/embed.go
- frontend/build/index.html
- Makefile
- go.mod
- go.sum
modified: []
key-decisions:
- "NATS JetStreamSyncInterval=0 (sync_interval: always) for single-node durability per Jepsen 2025"
- "WebSocket JWT via query parameter rather than header (browser WebSocket API limitation)"
- "Ephemeral JWT signing key (generated on startup) — will be persisted in a later plan"
- "NATS server requires Go 1.24+ — upgraded from Go 1.23 (auto-resolved by go get)"
- "Tournament validator is a stub (accepts all) — will validate against DB in auth plan"
patterns-established:
- "Embedded infrastructure: all services start in-process, no external dependencies"
- "Tournament-scoped state: all broadcasting and events keyed by tournament ID"
- "UUID validation on all NATS subject construction (security)"
- "Integration tests with httptest.NewServer and t.TempDir() for isolation"
requirements-completed: [ARCH-01, ARCH-04, ARCH-05, ARCH-06, ARCH-07]
# Metrics
duration: 15min
completed: 2026-03-01
---
# Phase 1 Plan 01: Project Scaffold + Core Infrastructure Summary
**Go binary embedding NATS JetStream (sync=always), LibSQL (WAL), WebSocket hub (JWT auth), chi HTTP server, and SvelteKit SPA via go:embed — all verified with 9 integration tests**
## Performance
- **Duration:** 15 min
- **Started:** 2026-03-01T02:27:38Z
- **Completed:** 2026-03-01T02:42:58Z
- **Tasks:** 2
- **Files modified:** 48
## Accomplishments
- Single Go binary compiles and runs with all infrastructure embedded — no external services required
- NATS JetStream with mandatory sync_interval=always for single-node durability (Jepsen 2025 finding)
- WebSocket hub authenticates via JWT query param, broadcasts tournament-scoped messages, drops slow consumers
- Health endpoint reports status of all subsystems (database, NATS, WebSocket)
- Full directory structure matching research recommendations with 30+ package stubs
## Task Commits
Each task was committed atomically:
1. **Task A1: Initialize Go module and dependency tree** - `af13732` (feat)
2. **Task A2: Implement core infrastructure** - `16caa12` (feat)
## Files Created/Modified
- `cmd/leaf/main.go` - Entry point: flags, startup orchestration, signal handling, graceful shutdown
- `cmd/leaf/main_test.go` - 9 integration tests covering all verification criteria
- `internal/nats/embedded.go` - Embedded NATS server with JetStream, AUDIT + STATE streams
- `internal/nats/publisher.go` - Tournament-scoped publisher with UUID validation
- `internal/server/server.go` - chi HTTP server with middleware, health endpoint, SPA handler
- `internal/server/ws/hub.go` - WebSocket hub with JWT auth, tournament scoping, broadcasting
- `internal/server/middleware/auth.go` - JWT validation middleware (Bearer header + raw token)
- `internal/server/middleware/role.go` - Role-based access control (admin > floor > viewer)
- `internal/server/middleware/bodylimit.go` - MaxBytesReader middleware (1MB default)
- `internal/store/db.go` - LibSQL open with WAL, foreign keys, busy timeout
- `internal/store/migrate.go` - Embedded SQL migration runner with dev-only migrations
- `frontend/embed.go` - go:embed handler with SPA fallback routing
- `frontend/build/index.html` - Stub HTML with Catppuccin Mocha dark theme colors
- `Makefile` - build, run, run-dev, test, frontend, all, clean targets
- `go.mod` / `go.sum` - Module definition with all dependencies pinned
## Decisions Made
- NATS server v2.12.4 requires Go 1.24+ — upgraded automatically from 1.23
- WebSocket JWT passed via query parameter (browser WebSocket API does not support custom headers)
- JWT signing key is ephemeral (random per startup) — will be persisted to disk in auth plan
- Tournament validator stub accepts all — real validation deferred to auth/tournament plans
- Added `run-dev` Makefile target for development mode with seed data
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Store package auto-populated by linter**
- **Found during:** Task A1 (directory structure creation)
- **Issue:** The db.go and migrate.go files were auto-populated with full implementations including migration runner, replacing my stub package declarations
- **Fix:** Kept the auto-populated implementations (correct and useful), removed my redundant migrations.go stub
- **Files modified:** internal/store/db.go, internal/store/migrate.go
- **Verification:** go build and go vet pass
- **Committed in:** af13732 (Task A1 commit)
**2. [Rule 3 - Blocking] Go 1.24 required by NATS server v2.12.4**
- **Found during:** Task A2 (dependency installation)
- **Issue:** nats-server v2.12.4 requires go >= 1.24.0, but go.mod specified 1.23.6
- **Fix:** go toolchain auto-resolved by upgrading go directive to 1.24.0 in go.mod
- **Files modified:** go.mod
- **Verification:** go build succeeds, all tests pass
- **Committed in:** 16caa12 (Task A2 commit)
---
**Total deviations:** 2 auto-fixed (2 blocking)
**Impact on plan:** Both auto-fixes were necessary for compilation. No scope creep.
## Issues Encountered
- Go 1.23.6 installed on system but NATS v2.12.4 required Go 1.24+ — resolved automatically by Go toolchain management
- Port 8080 remained bound after manual server test — cleaned up with lsof before re-test
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Infrastructure scaffold complete — ready for database schema (Plan 02), auth (Plan 03), and all subsequent plans
- All subsystems verified operational via integration tests
- Tournament-scoped architecture established from day one (MULTI-01)
## Self-Check: PASSED
All 16 created files verified present. Both commit hashes (af13732, 16caa12) found in git log. SUMMARY.md exists at expected path.
---
*Phase: 01-tournament-engine*
*Completed: 2026-03-01*