A social deduction party game built with Rust, Axum, HTMX, Tailwind CSS, and DaisyUI. Players must work together to identify the Dragon among them!
🎮 Play Now at dragonseeker.win
Dragonseeker is a real-time multiplayer social deduction game where players are assigned secret roles:
- Villagers: Know the secret word and must identify the Dragon
- Knights: Know a similar (but different) word and must identify the Dragon. Knights don't know they are knights - they think they are villagers!
- Dragon: Doesn't know the word and must blend in to survive
- Create Game: Host creates a new game and receives a unique shareable link
- Invite Players: Share the link with 2-11 friends (3-12 players total)
- Join Lobby: Players enter their nicknames to join the game
- Start Game: Host starts when everyone has joined
- Roles Assigned: Each player sees their role and word (Dragon sees "???")
- Say a Word: Each player takes turns saying ONE word that is similar to their secret word (but NOT the actual word itself). The Dragon must try to blend in by guessing what the word might be!
- Discussion Phase: Players discuss and analyze the words that were said to figure out who the Dragon is
- Voting Phase: Host initiates voting, everyone votes to eliminate a player
- Win Conditions:
- Villagers/Knights win if they eliminate the Dragon
- Dragon wins if they survive until ≤2 players remain OR correctly guess the word after elimination
- 🔗 Private Game Links - Each game gets a unique shareable URL
- ⚡ Real-time Updates - WebSocket-powered live game state
- ⚖️ Auto-balanced Roles - Fair distribution for 3-12 players
- 🗳️ Voting System - Democratic elimination with tie-breaker
- 🐉 Dragon Guess Mechanic - Last chance redemption
- 📱 Mobile Responsive - Works great on all devices
- 🎨 Modern UI - Beautiful interface with Tailwind + DaisyUI
- Rust 1.75+ (install via rustup)
- Cargo (comes with Rust)
cd app
cargo runThen open your browser to: http://localhost:8000
cd app
cargo build --release
./target/release/dragonseeker- Backend: Rust + Axum 0.7 + Tokio 1.40
- Frontend: HTMX 2.0.4 + Tailwind CSS + DaisyUI 4.12
- Templates: Askama 0.12 (compile-time Jinja2-like templates)
- WebSockets: tokio-tungstenite 0.24
- Deployment: Docker + Digital Ocean App Platform
app/
├── src/
│ ├── main.rs # Application entry point
│ ├── lib.rs # Library exports
│ ├── state.rs # AppState with Arc<RwLock<GameManager>>
│ │
│ ├── core/ # Game logic
│ │ ├── mod.rs
│ │ ├── constants.rs # Game settings & 500 word pairs
│ │ ├── player.rs # Player model
│ │ ├── roles.rs # Role assignment
│ │ ├── game_session.rs # Game state machine
│ │ └── game_manager.rs # Multi-game coordinator
│ │
│ ├── auth/ # Authentication
│ │ ├── mod.rs
│ │ ├── token.rs # HMAC-SHA256 tokens
│ │ └── middleware.rs # Auth extractor
│ │
│ ├── middleware/ # Web middleware
│ │ ├── mod.rs
│ │ ├── rate_limiter.rs # Per-IP rate limiting
│ │ └── security_headers.rs
│ │
│ ├── routes/ # HTTP handlers
│ │ ├── mod.rs
│ │ ├── game.rs # Create/join game
│ │ ├── lobby.rs # Lobby management
│ │ ├── gameplay.rs # Voting & gameplay
│ │ └── websocket.rs # WebSocket handler
│ │
│ ├── services/ # Business logic
│ │ ├── mod.rs
│ │ ├── voting.rs # Vote tallying
│ │ ├── win_conditions.rs # Win detection
│ │ └── game_state.rs # State transitions
│ │
│ └── models/ # DTOs
│ ├── mod.rs
│ ├── requests.rs # Request DTOs
│ └── responses.rs # Response DTOs
│
├── static/ # Frontend assets
│ ├── css/custom.css # Custom styles
│ ├── js/websocket-client.js # WebSocket manager
│ └── js/htmx-config.js # HTMX setup
│
├── templates/ # HTML templates
│ ├── base.html # Base template
│ ├── index.html # Landing page
│ ├── join.html # Join page
│ ├── lobby.html # Game lobby
│ ├── game.html # Active game
│ └── results.html # Game over
│
├── Cargo.toml # Project manifest
└── Cargo.lock # Dependency lock
LOBBY → (host starts) → PLAYING → (host votes) → VOTING
→ (all voted) → DRAGON_GUESS or FINISHED
→ (check win) → FINISHED or PLAYING
| Players | Dragon | Knights | Villagers |
|---|---|---|---|
| 3-4 | 1 | 0 | 2-3 |
| 5-6 | 1 | 1 | 3-4 |
| 7-8 | 1 | 2 | 4-5 |
| 9-10 | 1 | 3 | 5-6 |
| 11-12 | 1 | 4 | 6-7 |
GET /- Landing pagePOST /api/games/create- Create new gameGET /game/{game_id}/join- Join pagePOST /api/games/{game_id}/join- Join game
GET /game/{game_id}/lobby- Lobby pagePOST /api/games/{game_id}/start- Start game (host only)
GET /game/{game_id}/play- Game interfacePOST /api/games/{game_id}/start-voting- Start voting phasePOST /api/games/{game_id}/vote- Submit votePOST /api/games/{game_id}/guess-word- Dragon word guessGET /game/{game_id}/results- Results page
WS /ws/{game_id}/{player_id}- Real-time game updates
GET /health- Health check endpoint
The project includes a Dockerfile for containerized deployment:
docker build -t dragonseeker .
docker run -p 8000:8000 dragonseekerThe app is configured for automatic deployment:
- Push to your GitHub repository
- Digital Ocean auto-deploys on push to
mainbranch - Configuration in
.do/app.yaml
This project uses Rust's built-in development tools for testing, linting, and type checking.
All development dependencies are managed through Cargo and are automatically installed when you build the project.
Run all tests with cargo:
cd app
cargo test # Run all tests
cargo test -- --nocapture # Show output
cargo test -- --test-threads=1 # Sequential executionThe test suite includes 119 tests covering:
- Core game logic (player, roles, game session, game manager)
- Authentication and middleware (HMAC tokens, rate limiting, security headers)
- Business logic (voting, win conditions, game state transitions)
- Route handlers and models
Format code with cargo fmt:
cd app
cargo fmt # Format all Rust files
cargo fmt --check # Check if formattedCheck code quality with clippy:
cd app
cargo clippy # Check for issues
cargo clippy -- -D warnings # Treat warnings as errors
cargo fix # Auto-fix issuesClippy checks for:
- Common bugs and anti-patterns
- Performance issues
- Idiomatic Rust patterns
- Unsafe code usage
- Documentation quality
Rust has compile-time type checking built-in. Simply run:
cd app
cargo check # Check for type errors
cargo build # Full compilation checkBefore committing code, run:
cd app
cargo fmt --check # Verify formatting
cargo clippy -- -D warnings # Check for issues
cargo test # Run all tests
cargo build --release # Verify release buildThe server uses structured logging with different verbosity levels:
Production (default): Only warnings and errors are logged
cargo run --releaseDevelopment: Enable verbose debug logging
ENVIRONMENT=development cargo runYou can also override the log level with the RUST_LOG environment variable:
RUST_LOG=debug cargo run # Debug logs
RUST_LOG=info cargo run # Info logs
RUST_LOG=warn cargo run # Warn logs (production default)Edit app/src/core/constants.rs to add or modify the word pairs:
pub const WORD_PAIRS: &[(&str, &str)] = &[
("elephant", "mammoth"),
("telescope", "binoculars"),
// Add your word pairs here...
];Modify app/src/core/constants.rs:
pub const MIN_PLAYERS: usize = 3; // Minimum players to start
pub const MAX_PLAYERS: usize = 12; // Maximum players allowed
pub const GAME_TTL_SECONDS: u64 = 3600; // Game cleanup time (1 hour)Potential features to add:
- ⏱️ Discussion timer with countdown
- 💬 In-game text chat for remote play
- 📊 Game history and statistics
- 📚 Custom word lists by category
- 👀 Spectator mode
- 🔊 Sound effects and animations
- 📱 Progressive Web App (PWA) for mobile
- 🌐 Multi-language support
- 🎭 Custom role abilities (e.g., Knights get hints)
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). See LICENSE.md for details.
The AGPL-3.0 is a strong copyleft license that requires anyone who runs a modified version of this software as a network service to make the source code available to users of that service.
Contributions are welcome! Feel free to:
- Report bugs
- Suggest new features
- Submit pull requests
Play the live game at dragonseeker.win!
Or run locally by starting the server and visiting http://localhost:8000:
cd app
cargo runEnjoy the game! 🐉⚔️🏘️