Felt - Go/Svelte project
Find a file
2026-03-01 08:39:34 +01:00
.planning docs(01-14): complete Frontend Tables Tab + More Tab plan 2026-03-01 08:30:16 +01:00
cmd/leaf feat(01-04): add clock warnings, API routes, tests, and server wiring 2026-03-01 03:56:23 +01:00
docs Initial docs/project description including financial models 2026-02-28 15:37:21 +01:00
frontend feat(01-14): implement More tab with templates, blind editor, wizard, settings, audit 2026-03-01 08:26:12 +01:00
internal fix(tests): use per-test in-memory DB to prevent shared state conflicts 2026-03-01 08:16:50 +01:00
.gitignore feat(01-01): initialize Go module, dependency tree, and project scaffold 2026-03-01 03:34:44 +01:00
go.mod feat(01-07): implement player CRUD, search, merge, CSV import, QR codes 2026-03-01 04:32:13 +01:00
go.sum feat(01-07): implement player CRUD, search, merge, CSV import, QR codes 2026-03-01 04:32:13 +01:00
Makefile feat(01-10): SvelteKit frontend scaffold with Catppuccin theme and clients 2026-03-01 03:54:29 +01:00
README.md docs: add README with project overview, architecture, and getting started 2026-03-01 08:39:34 +01:00


♠ ♥ ♣ ♦

Felt

The Operating System for Poker Venues
Run a complete tournament offline on a €100 device — it just works.

FeaturesArchitectureGetting StartedDevelopmentProject StructureLicense


Felt replaces the fragmented tools poker venues cobble together — TDD, whiteboards, spreadsheets, Blind Valet, BravoPokerLive — with one integrated platform. From a 3-table Copenhagen bar to a 40-table casino floor.

TDD's brain. Linear's face. Dark-room ready.

Features

Tournament Engine

  • Clock Engine — Countdown, blind levels, antes, breaks, chip-ups, pause/resume, hand-for-hand. 100ms tick resolution with configurable warnings
  • Blind Structure Wizard — Generate playable structures from target duration, starting chips, and game type. Mixed-game rotation, big-blind antes, chip-up breaks
  • Financial Engine — Buy-ins, rebuys, add-ons, bounties (standard + PKO), payouts, rake — all int64 cents, never float64. CI-gated: sum of payouts always equals prize pool
  • ICM Calculator — Exact Malmuth-Harville for ≤10 players, Monte Carlo (100K iterations) for 11+. Chop/deal support: ICM, chip-chop, even-chop, custom split, partial chop
  • Player Management — Full-text search, buy-in with auto-seating, bust-out with hitman tracking, undo with full re-ranking, CSV import/export, QR codes
  • Table & Seating — Configurable layouts, auto-balancing with clockwise distance fairness, break table (fully automatic), tap-tap seat moves
  • Multi-Tournament — Run simultaneous tournaments with fully independent state (clocks, financials, players, tables)
  • Audit Trail — Every state-changing action logged. Any financial transaction or bust-out undoable with full re-ranking

Operator UI

  • Mobile-first, touch-native — 48px touch targets, FAB with contextual actions, persistent header with tournament switcher
  • Catppuccin Mocha dark theme (dark-room ready) with Latte light alternative
  • Overview — Large clock display, player counts, table balance status, financial summary, activity feed
  • Financials — Prize pool breakdown, transaction history, payout preview, bubble prize, chop/deal flow
  • Players — Typeahead search, buy-in flow (search → auto-seat → confirm → receipt), bust-out flow (minimal taps)
  • Tables — SVG oval table view with seat positions, list view for large tournaments, balancing panel
  • Templates — LEGO-style building blocks: chip sets, blind structures, payout structures, buy-in configs, assembled into reusable tournament templates

Architecture

  • Single Go binary — Embeds HTTP server, SvelteKit frontend, NATS JetStream, and LibSQL. One process, zero dependencies
  • Offline-first — Entire operation works without internet. Cloud is never a dependency during a tournament
  • WebSocket real-time — State changes propagate to all clients within 100ms
  • PIN authentication — Operators log in with 4-6 digit PINs. Argon2id hashing, rate limiting, JWT sessions

Architecture

┌──────────────────────────────────────────────┐
│                   Leaf Node                   │
│                                               │
│  ┌──────────┐  ┌──────────┐  ┌────────────┐  │
│  │  SvelteKit │  │   Go     │  │   NATS     │  │
│  │  Frontend │──│  Server   │──│  JetStream │  │
│  │  (embed)  │  │  (HTTP)   │  │ (embedded) │  │
│  └──────────┘  └──────────┘  └────────────┘  │
│                      │                        │
│                ┌─────┴─────┐                  │
│                │   LibSQL   │                  │
│                │  (SQLite)  │                  │
│                └───────────┘                  │
└──────────────────────────────────────────────┘
         │              │              │
    ┌────┴────┐    ┌────┴────┐    ┌────┴────┐
    │ Operator │    │ Display │    │ Player  │
    │  Phone   │    │  TV/Pi  │    │  Phone  │
    └─────────┘    └─────────┘    └─────────┘

Stack:

Layer Technology Why
Backend Go 1.24 Single binary, ARM cross-compile, goroutine concurrency
Frontend SvelteKit 2 Reactive UI, SPA for operator, SSR for public pages
Database LibSQL (SQLite) Embedded, zero-config, WAL mode, crash-safe
Messages NATS JetStream Embedded, 10MB RAM, persistent queuing, ordered replay
Auth Argon2id + JWT Offline PIN login, role-based access
Theme Catppuccin Mocha Dark-room optimized, systematic accent palette

Getting Started

Prerequisites

  • Go 1.24+ with CGO enabled
  • Node.js 20+ and npm
  • Linux (developed on Debian/Ubuntu in LXC)

Build & Run

# Clone the repository
git clone ssh://git@your-forgejo-instance/mikkel/felt.git
cd felt

# Build frontend + backend
make all

# Run in development mode
make run-dev

# Run tests
make test

The server starts at http://localhost:8080. Open it on any device on the local network.

First Run

  1. Navigate to http://<host>:8080
  2. Log in with the default operator PIN (shown in terminal on first start)
  3. Go to More → Templates to set up your tournament building blocks
  4. Create a tournament from a template
  5. Start managing your tournament

Development

Commands

make build      # Build Go binary (CGO_ENABLED=1)
make run        # Build and run on :8080
make run-dev    # Build and run in dev mode
make test       # Run all Go tests
make frontend   # Build SvelteKit frontend
make all        # Build frontend + backend
make clean      # Remove binary and data

Environment Variables

Variable Default Description
DATA_DIR ./data SQLite database and data storage
ADDR :8080 HTTP listen address

Project Structure

felt/
├── cmd/leaf/              # Main binary entrypoint
│   └── main.go            # Server startup, wiring, graceful shutdown
├── internal/              # Application packages
│   ├── audit/             # Append-only audit trail + undo engine
│   ├── auth/              # PIN auth (Argon2id) + JWT tokens
│   ├── blind/             # Blind structure + wizard generation
│   ├── clock/             # Tournament clock engine (100ms resolution)
│   ├── financial/         # Buy-in/rebuy/addon/payout/ICM/chop engine
│   ├── nats/              # Embedded NATS JetStream server + publisher
│   ├── player/            # Player CRUD, search, rankings, CSV, QR
│   ├── seating/           # Table management, balancing, break table
│   ├── server/            # HTTP server, middleware, WebSocket hub
│   │   ├── middleware/     # Auth + role middleware
│   │   ├── routes/        # API route handlers
│   │   └── ws/            # WebSocket hub + client management
│   ├── store/             # Database setup, migrations, queries
│   │   ├── migrations/    # SQL migration files
│   │   └── queries/       # Named SQL queries
│   ├── template/          # Building blocks: chipsets, payouts, buyins
│   └── tournament/        # Tournament lifecycle, multi-tournament, state
├── frontend/              # SvelteKit operator UI
│   ├── src/
│   │   ├── lib/
│   │   │   ├── api/       # HTTP API client (auto-JWT, error handling)
│   │   │   ├── components/ # Svelte components (clock, tables, FAB, etc.)
│   │   │   ├── stores/    # Svelte 5 runes state (tournament, auth, multi)
│   │   │   ├── theme/     # Catppuccin CSS custom properties
│   │   │   └── ws/        # WebSocket client with auto-reconnect
│   │   └── routes/        # SvelteKit pages (overview, players, tables, etc.)
│   └── build/             # Compiled frontend (go:embed)
├── Makefile               # Build commands
├── go.mod
└── go.sum

Design Philosophy

"TDD's brain, Linear's face."

Match The Tournament Director's depth and power while looking and feeling like a modern premium product.

  • Dark-room ready — Catppuccin Mocha dark theme optimized for dimly lit poker rooms
  • Touch-native — 48px minimum touch targets, FAB for frequent actions, swipe-friendly
  • Glanceable — Information-dense without clutter. Monospace numbers, semantic color coding
  • Offline-first — Internet is never required during operation. Everything runs locally
  • int64 cents — All monetary values are integer cents. No floating point. Ever. CI-gated

Roadmap

Phase Description Status
1 Tournament Engine In Progress
2 Display Views + Player PWA Planned
3 Core Sync + Platform Identity Planned
4 Digital Signage + Events Engine Planned
5 Leagues, Seasons + Regional Planned
6 TDD Migration Planned
7 Hardware Leaf (ARM64 + Offline) Planned

License

Proprietary. All rights reserved.


Built with Go, SvelteKit, LibSQL, and NATS JetStream.
Designed for dark rooms and fast decisions.