|
|
||
|---|---|---|
| .planning | ||
| cmd/leaf | ||
| docs | ||
| frontend | ||
| internal | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
♠ ♥ ♣ ♦
Felt
The Operating System for Poker Venues
Run a complete tournament offline on a €100 device — it just works.
Features • Architecture • Getting Started • Development • Project Structure • License
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
int64cents, neverfloat64. 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
- Navigate to
http://<host>:8080 - Log in with the default operator PIN (shown in terminal on first start)
- Go to More → Templates to set up your tournament building blocks
- Create a tournament from a template
- 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.