278 lines
16 KiB
Markdown
278 lines
16 KiB
Markdown
# Stack Research
|
|
|
|
**Domain:** Edge-cloud poker venue management platform (ARM64 SBC + cloud hybrid)
|
|
**Researched:** 2026-02-28
|
|
**Confidence:** MEDIUM-HIGH (core stack verified via official sources; peripheral libraries verified via pkg.go.dev and GitHub releases; CGO cross-compilation complexity is a known risk requiring phase-specific validation)
|
|
|
|
---
|
|
|
|
## Recommended Stack
|
|
|
|
### Core Technologies
|
|
|
|
| Technology | Version | Purpose | Why Recommended |
|
|
|------------|---------|---------|-----------------|
|
|
| Go | 1.26 | Backend runtime (Leaf + Core shared codebase) | Single binary deployment, ARM64 cross-compilation, goroutine concurrency for real-time tournament state, excellent stdlib HTTP. Released Feb 10, 2026. |
|
|
| SvelteKit | 2.53.x | Operator UI, player PWA, admin dashboard | Single codebase for SPA, SSR, and PWA modes. SvelteKit 2 + Svelte 5 runes are production-stable. Adapter-static for Go embed, adapter-node for standalone. |
|
|
| Svelte | 5.53.x | Frontend framework | Runes reactivity model ($state, $derived, $effect) handles high-frequency real-time data (100ms tournament clock) without store complexity. Svelte 5 stable since Oct 2024. |
|
|
| NATS Server | 2.12.4 | Embedded message broker on Leaf; clustered on Core | Embeds directly into Go binary (~10MB RAM overhead), JetStream provides offline-durable queuing, ordered replay on reconnect, KV store. ARM64 native packages available. |
|
|
| LibSQL (go-libsql) | unreleased / CGO | Embedded SQLite-compatible DB on Leaf | SQLite-compatible with built-in replication support. Supports linux/arm64 natively via precompiled binaries. CGO_ENABLED=1 required. |
|
|
| PostgreSQL | 16 | Relational DB on Core | Standard choice for Core; multi-tenant RLS, full-text search for player lookup, proven at scale. LibSQL mirrors for sync path. |
|
|
| Tailwind CSS | 4.x | UI styling | v4 uses Vite plugin (no PostCSS config needed), 100x faster incremental builds, CSS-native config. Pairs naturally with SvelteKit's Vite build pipeline. |
|
|
| Netbird | latest | WireGuard mesh overlay network | Self-hosted, provides mesh VPN + reverse proxy + DNS + SSH + firewall policies in one platform. Zero-config peer connection through NAT. ARM64 client supported. |
|
|
| Authentik | 2026.2.x | Self-hosted OIDC Identity Provider | Integrates natively with Netbird self-hosted. Provides SSO for operator login, LDAP fallback, Apache 2.0. Requires PostgreSQL + Redis; runs in LXC on Core. |
|
|
|
|
### Supporting Libraries — Go Backend
|
|
|
|
| Library | Version | Purpose | When to Use |
|
|
|---------|---------|---------|-------------|
|
|
| github.com/go-chi/chi/v5 | v5.2.5 | HTTP router | All Leaf and Core HTTP handlers. Lightweight, fully net/http compatible, composable middleware, no magic. |
|
|
| github.com/nats-io/nats.go | v1.49.0 | NATS client (JetStream API) | Publishing, consuming, and managing JetStream streams from application code. Uses the new jetstream sub-package API. |
|
|
| github.com/nats-io/nats-server/v2 | v2.12.4 | Embedded NATS server | Leaf embeds this directly via server.NewServer() + EnableJetStream(). Not used on Core (standalone server process). |
|
|
| github.com/pressly/goose/v3 | v3.27.0 | Database migrations | Runs schema migrations at startup via embed.FS. Supports SQLite + PostgreSQL with same migration files. |
|
|
| github.com/sqlc-dev/sqlc | v1.30.0 | Type-safe SQL code generation | Generate Go structs and query functions from raw SQL. Eliminates ORM overhead, keeps SQL as SQL. |
|
|
| github.com/coder/websocket | v1.8.14 | WebSocket server | Real-time push to operator UI and player PWA. Actively maintained successor to nhooyr/websocket. Context-aware, zero-allocation. |
|
|
| github.com/golang-jwt/jwt/v5 | latest | JWT token handling | Offline PIN-based auth on Leaf (no network dependency). Validates tokens from Authentik OIDC on Core. |
|
|
| go.opentelemetry.io/otel | 1.x | Observability | Structured tracing for state machine transitions, tournament operations. Add otelchi for per-request span creation. |
|
|
| github.com/riandyrn/otelchi | latest | OpenTelemetry middleware for chi | Automatic HTTP span creation. Plug into chi middleware chain. |
|
|
|
|
### Supporting Libraries — SvelteKit Frontend
|
|
|
|
| Library | Version | Purpose | When to Use |
|
|
|---------|---------|---------|-------------|
|
|
| @vite-pwa/sveltekit | latest | PWA + service worker | Player PWA offline caching, installable shell. Wraps Workbox. Required for offline player access. |
|
|
| vite-plugin-pwa | latest | PWA build tooling | Underlying PWA config for manifest, service worker generation. |
|
|
| @tailwindcss/vite | 4.x | Tailwind v4 Vite integration | Add before sveltekit() in vite.config. CSS-native config via app.css @import "tailwindcss". |
|
|
| svelte-sonner | latest | Toast notifications | Operator action feedback (seat assignments, registration, bust-outs). Lightweight, accessible. |
|
|
| @lucide/svelte | latest | Icon set | Consistent iconography. Tree-shakeable, Svelte-native bindings. |
|
|
|
|
### Development Tools
|
|
|
|
| Tool | Purpose | Notes |
|
|
|------|---------|-------|
|
|
| Task (Taskfile) | Build orchestration | Define `build:leaf`, `build:core`, `build:frontend`, `cross-compile:arm64` tasks. Replaces Makefile with YAML syntax. |
|
|
| Docker buildx | ARM64 cross-compilation | For CGO-enabled builds targeting linux/arm64. Use `--platform linux/arm64` with aarch64-linux-gnu-gcc cross-compiler in container. |
|
|
| air | Go live reload (dev) | `github.com/air-verse/air` — watches Go files, rebuilds on change. Dev only. |
|
|
| golangci-lint | Go linting | Runs multiple linters. Critical for enforcing error handling patterns in state machine code. |
|
|
| playwright | E2E testing | Test operator UI flows. Svelte-compatible. |
|
|
| sqlc | SQL → Go codegen | Run as part of build pipeline. Check generated files into git. |
|
|
|
|
---
|
|
|
|
## Installation
|
|
|
|
### Go Backend
|
|
|
|
```bash
|
|
# Initialize module
|
|
go mod init felt
|
|
|
|
# Core router and middleware
|
|
go get github.com/go-chi/chi/v5@v5.2.5
|
|
go get github.com/go-chi/cors
|
|
|
|
# NATS (client + embedded server)
|
|
go get github.com/nats-io/nats.go@v1.49.0
|
|
go get github.com/nats-io/nats-server/v2@v2.12.4
|
|
|
|
# Database — LibSQL (CGO required)
|
|
# Note: CGO_ENABLED=1 and linux/arm64 cross-compiler required for Leaf builds
|
|
go get github.com/tursodatabase/go-libsql
|
|
|
|
# Database — PostgreSQL for Core
|
|
go get github.com/jackc/pgx/v5
|
|
|
|
# Migrations and query gen
|
|
go get github.com/pressly/goose/v3@v3.27.0
|
|
# sqlc: install as tool
|
|
go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.30.0
|
|
|
|
# Auth
|
|
go get github.com/golang-jwt/jwt/v5
|
|
|
|
# WebSocket
|
|
go get github.com/coder/websocket@v1.8.14
|
|
|
|
# Observability
|
|
go get go.opentelemetry.io/otel
|
|
go get go.opentelemetry.io/otel/sdk
|
|
go get github.com/riandyrn/otelchi
|
|
```
|
|
|
|
### SvelteKit Frontend
|
|
|
|
```bash
|
|
# Scaffold
|
|
npx sv create felt-frontend
|
|
# Choose: SvelteKit, TypeScript, Tailwind
|
|
|
|
# Or manually:
|
|
npm create svelte@latest felt-frontend
|
|
|
|
# Tailwind v4
|
|
npm install tailwindcss @tailwindcss/vite
|
|
|
|
# PWA
|
|
npm install -D @vite-pwa/sveltekit vite-plugin-pwa
|
|
|
|
# UI libraries
|
|
npm install svelte-sonner @lucide/svelte
|
|
```
|
|
|
|
---
|
|
|
|
## Alternatives Considered
|
|
|
|
| Recommended | Alternative | When to Use Alternative |
|
|
|-------------|-------------|-------------------------|
|
|
| chi (router) | Gin, Echo, Fiber | Gin if you want more batteries-included middleware and faster onboarding. Fiber if raw throughput benchmarks matter more than stdlib compatibility. Chi chosen here because it's pure net/http, no magic, easy to embed with NATS server in same binary. |
|
|
| LibSQL (go-libsql) | mattn/go-sqlite3 | go-sqlite3 if you never need replication or remote sync. go-libsql is SQLite-compatible but adds replication capability needed for Leaf→Core sync path. |
|
|
| LibSQL (go-libsql) | modernc.org/sqlite | modernc if CGO is unacceptable (pure Go, no cross-compile issues). Tradeoff: no replication, pure-Go performance is slower, and you lose LibSQL's sync protocol. |
|
|
| goose | golang-migrate | golang-migrate is fine but goose has cleaner embed.FS support and the sqlc community uses it as the reference migration tool. |
|
|
| coder/websocket | gorilla/websocket | gorilla/websocket if you need RFC 7455 edge cases or have existing gorilla-dependent code. gorilla is widely used but coder/websocket is the modern, context-aware successor. |
|
|
| NATS JetStream | Redis Streams, RabbitMQ | Redis Streams if you already have Redis in the stack. RabbitMQ for complex enterprise routing. NATS chosen because it embeds in the Go binary (no separate process on Leaf), runs on 10MB RAM, and handles offline-queued replay natively. |
|
|
| Authentik | Keycloak, Authelia | Keycloak if you need SAML federation or very large enterprise deployments. Authelia if you want lightweight forward-auth only. Authentik chosen for OIDC depth, active development, and documented Netbird integration. |
|
|
| Tailwind CSS v4 | UnoCSS, vanilla CSS | UnoCSS if Tailwind v4's Rust engine still has edge-case gaps in your toolchain. Vanilla CSS with CSS custom properties if the design system is simple. Tailwind v4 chosen for its systematic utility classes matching the dense information design requirement. |
|
|
| SvelteKit | React/Next.js, Vue/Nuxt | React/Next.js if you have an existing team with React expertise. SvelteKit chosen for smaller bundle sizes (critical for Pi Zero display nodes loading over WiFi), built-in PWA path, and Svelte 5 runes handling high-frequency clock updates without virtual DOM overhead. |
|
|
|
|
---
|
|
|
|
## What NOT to Use
|
|
|
|
| Avoid | Why | Use Instead |
|
|
|-------|-----|-------------|
|
|
| gorilla/websocket (new code) | Unmaintained since 2023, no context support, no concurrent writes | github.com/coder/websocket (maintained successor) |
|
|
| gorm | ORM magic hides SQL, bad for complex tournament state queries, generates inefficient queries, fights with LibSQL's CGO interface | sqlc for generated type-safe queries, raw database/sql when needed |
|
|
| modernc.org/sqlite for Leaf | Pure-Go SQLite has no replication — you lose the LibSQL sync protocol that enables Leaf→Core data replication | tursodatabase/go-libsql (CGO, linux/arm64 prebuilt) |
|
|
| React/Next.js | Heavy bundle — Pi Zero 2W (512MB RAM) running Chromium kiosk will struggle; Svelte compiles to vanilla JS with no runtime | SvelteKit + Svelte 5 |
|
|
| Svelte 4 / SvelteKit 1 | End of active development; Svelte 5 runes are the current API; v4 stores pattern has known SSR shared-state bugs | Svelte 5 + SvelteKit 2 |
|
|
| Tailwind CSS v3 | Requires PostCSS config, slower builds, JS-based config. v4 drops all of this and integrates cleaner with Vite | Tailwind CSS v4 with @tailwindcss/vite plugin |
|
|
| OIDC-only auth on Leaf | If Core/internet is down, OIDC token validation fails → operators locked out | JWT-based offline PIN auth on Leaf, OIDC only when online |
|
|
| Global Netbird cloud | Introduces Netbird as a third-party MITM for network control plane | Self-hosted Netbird management server on Core (Hetzner Proxmox LXC) |
|
|
| Docker on Leaf (ARM64) | Docker daemon adds ~100MB RAM overhead on a 4-8GB device; unnecessary abstraction for a dedicated appliance | Bare systemd services; Go single binary + SvelteKit embedded build |
|
|
|
|
---
|
|
|
|
## Stack Patterns by Variant
|
|
|
|
**For Leaf Node (ARM64 SBC, offline-first):**
|
|
- Go binary with embedded NATS server + JetStream store on NVMe
|
|
- LibSQL (go-libsql, CGO) with goose migrations at startup
|
|
- SvelteKit build embedded via `//go:embed all:build` in Go binary
|
|
- Single systemd service: `felt-leaf`
|
|
- Offline PIN auth via JWT; Authentik OIDC optional when online
|
|
- CGO cross-compile: `GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc go build`
|
|
|
|
**For Core Node (Hetzner Proxmox LXC, always-online):**
|
|
- Go binary (no embedded NATS — connects to standalone NATS cluster)
|
|
- PostgreSQL (pgx/v5 driver)
|
|
- NATS JetStream cluster for multi-venue message routing
|
|
- Authentik + Netbird management server as separate LXC containers
|
|
- Standard AMD64 build: `GOOS=linux GOARCH=amd64 go build`
|
|
|
|
**For Display Nodes (Pi Zero 2W, Chromium kiosk):**
|
|
- No custom software on the node itself
|
|
- Raspberry Pi OS Lite + X11 + Chromium in `--kiosk` mode
|
|
- Chromium points to `http://leaf.local/display/{node-id}` (served by Leaf Go binary)
|
|
- Display content is pure SvelteKit SPA pages served from the Leaf
|
|
- Node management (which view to show) handled via NATS KV on Leaf
|
|
|
|
**If CGO cross-compilation proves painful in CI:**
|
|
- Use Docker buildx with `FROM --platform=linux/arm64` base and native arm64 runner
|
|
- OR accept CGO complexity and use `aarch64-linux-gnu-gcc` in a standard amd64 CI runner
|
|
- Do NOT switch to modernc.org/sqlite — losing replication is worse than cross-compile friction
|
|
|
|
---
|
|
|
|
## Version Compatibility
|
|
|
|
| Package | Compatible With | Notes |
|
|
|---------|-----------------|-------|
|
|
| go-libsql (unreleased) | Go 1.26, linux/arm64 | No tagged releases; pin to commit hash in go.mod. CGO_ENABLED=1 mandatory. |
|
|
| @vite-pwa/sveltekit | SvelteKit 2.x, Vite 5.x | From v0.3.0+ supports SvelteKit 2. |
|
|
| goose v3.27.0 | Go 1.25+ | Requires Go 1.25 minimum per v3.27.0 release notes. Go 1.26 is compatible. |
|
|
| chi v5.2.5 | Go 1.22+ | Standard net/http; any Go 1.22+ supported. |
|
|
| Tailwind v4 | Vite 5.x, SvelteKit 2.x | @tailwindcss/vite must be listed before sveltekit() in vite.config plugins array. |
|
|
| NATS server v2.12.4 | nats.go v1.49.0 | Use matching client and server versions. Server v2.12.x is compatible with client v1.49.x. |
|
|
| Svelte 5.53.x | SvelteKit 2.53.x | Must use matching Svelte 5 + SvelteKit 2. Svelte 4 / SvelteKit 1 are not compatible targets. |
|
|
|
|
---
|
|
|
|
## Critical Build Notes
|
|
|
|
### LibSQL CGO Cross-Compilation
|
|
|
|
go-libsql has no tagged releases on GitHub. You must pin to a specific commit:
|
|
|
|
```bash
|
|
go get github.com/tursodatabase/go-libsql@<commit-hash>
|
|
```
|
|
|
|
Cross-compilation for ARM64 requires the GNU cross-compiler toolchain:
|
|
|
|
```bash
|
|
# On Ubuntu/Debian CI
|
|
apt-get install gcc-aarch64-linux-gnu
|
|
|
|
# Build command
|
|
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 \
|
|
CC=aarch64-linux-gnu-gcc \
|
|
go build -o felt-leaf ./cmd/leaf
|
|
```
|
|
|
|
Alternative (recommended for CI consistency): Use Docker buildx with an ARM64 base image to build natively, avoiding cross-compiler dependency management.
|
|
|
|
### SvelteKit Static Embed in Go Binary
|
|
|
|
```go
|
|
//go:embed all:frontend/build
|
|
var frontendFS embed.FS
|
|
|
|
// In HTTP handler:
|
|
sub, _ := fs.Sub(frontendFS, "frontend/build")
|
|
http.FileServer(http.FS(sub))
|
|
```
|
|
|
|
SvelteKit must be built with `@sveltejs/adapter-static` for full embed. The Leaf serves all frontend assets from its single binary — no separate static file server.
|
|
|
|
### NATS Embedded Server Setup
|
|
|
|
```go
|
|
opts := &server.Options{
|
|
Port: 4222,
|
|
Host: "127.0.0.1",
|
|
JetStream: true,
|
|
StoreDir: "/data/nats/jetstream", // on NVMe
|
|
}
|
|
ns, err := server.NewServer(opts)
|
|
ns.Start()
|
|
nc, _ := nats.Connect(ns.ClientURL())
|
|
js, _ := jetstream.New(nc)
|
|
```
|
|
|
|
---
|
|
|
|
## Sources
|
|
|
|
- Go 1.26 release: https://go.dev/blog/go1.26 — HIGH confidence
|
|
- NATS Server v2.12.4: https://github.com/nats-io/nats-server/releases — HIGH confidence (official)
|
|
- NATS server embed API: https://pkg.go.dev/github.com/nats-io/nats-server/v2/server — HIGH confidence
|
|
- nats.go v1.49.0: https://github.com/nats-io/nats.go/releases — HIGH confidence
|
|
- go-libsql ARM64 support: https://github.com/tursodatabase/go-libsql — HIGH confidence (official repo)
|
|
- chi v5.2.5: https://github.com/go-chi/chi/tree/v5.2.3 + pkg.go.dev — HIGH confidence
|
|
- goose v3.27.0: https://pkg.go.dev/github.com/pressly/goose/v3 + releases — HIGH confidence
|
|
- sqlc v1.30.0: https://github.com/sqlc-dev/sqlc/releases — HIGH confidence
|
|
- coder/websocket v1.8.14: https://github.com/coder/websocket — HIGH confidence
|
|
- SvelteKit 2.53.2: https://www.npmjs.com/package/@sveltejs/kit — HIGH confidence
|
|
- Svelte 5.53.5: https://www.npmjs.com/package/svelte — HIGH confidence
|
|
- Tailwind v4 Vite integration: https://tailwindcss.com/docs/guides/sveltekit — HIGH confidence
|
|
- @vite-pwa/sveltekit: https://github.com/vite-pwa/sveltekit — MEDIUM confidence (version not pinned)
|
|
- Authentik Netbird integration: https://docs.netbird.io/selfhosted/identity-providers/authentik — MEDIUM confidence
|
|
- CGO ARM64 cross-compilation: https://forum.golangbridge.org/t/cross-compiling-go-with-cgo-for-arm64/38794 — MEDIUM confidence (community)
|
|
- Go embed + SvelteKit: https://www.liip.ch/en/blog/embed-sveltekit-into-a-go-binary — MEDIUM confidence (verified pattern, widely cited)
|
|
- Pi Zero 2W Chromium kiosk: https://gist.github.com/lellky/673d84260dfa26fa9b57287e0f67d09e — MEDIUM confidence
|
|
|
|
---
|
|
|
|
*Stack research for: Felt — Edge-cloud poker venue management platform*
|
|
*Researched: 2026-02-28*
|