221 lines
10 KiB
Markdown
221 lines
10 KiB
Markdown
<p align="center">
|
|
<br>
|
|
<strong style="font-size: 2.5em">♠ ♥ ♣ ♦</strong>
|
|
<br><br>
|
|
</p>
|
|
|
|
<h1 align="center">Felt</h1>
|
|
|
|
<p align="center">
|
|
<strong>The Operating System for Poker Venues</strong>
|
|
<br>
|
|
<em>Run a complete tournament offline on a €100 device — it just works.</em>
|
|
</p>
|
|
|
|
<p align="center">
|
|
<a href="#features">Features</a> •
|
|
<a href="#architecture">Architecture</a> •
|
|
<a href="#getting-started">Getting Started</a> •
|
|
<a href="#development">Development</a> •
|
|
<a href="#project-structure">Project Structure</a> •
|
|
<a href="#license">License</a>
|
|
</p>
|
|
|
|
---
|
|
|
|
**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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
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.
|
|
|
|
---
|
|
|
|
<p align="center">
|
|
<sub>Built with Go, SvelteKit, LibSQL, and NATS JetStream.</sub>
|
|
<br>
|
|
<sub>Designed for dark rooms and fast decisions.</sub>
|
|
</p>
|