- Rust 53%
- Python 16.6%
- Nix 12.7%
- Kotlin 12.6%
- Shell 4.9%
- Other 0.2%
| android | ||
| feeder | ||
| hosts | ||
| lib | ||
| modules | ||
| scripts | ||
| .gitignore | ||
| debug-auth.sh | ||
| DEPLOYMENT.md | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
Location-Based Game Platform
A multiplayer location-based game built with Rust + PostgreSQL + Android. Players move around a city, see each other on a map, and complete location-based challenges.
Architecture
Android Clients
↓ (sensor data + API key)
Ingestion Service (Rust/Rocket, port 8000)
↓ (SQL)
PostgreSQL + PostGIS
↑ (SQL queries)
Game Service (Rust/Rocket, port 8001)
↑↓ (JSON REST API + API key)
Android Clients (game state display)
Tech Stack
- Backend: Rust + Rocket 0.5.1
- Database: PostgreSQL + PostGIS (spatial indexing)
- ORM/Database: SQLx (type-safe async queries)
- Mobile: Kotlin (Android)
- Deployment: NixOS + Systemd
- Monitoring: Prometheus metrics built-in
- Auth: Simple API key (Bearer token)
Quick Start
Prerequisites
- Nix (for reproducible builds)
- Git
- For non-NixOS: PostgreSQL + Rust toolchain
Option 1: NixOS System Deployment (Recommended)
Add to your /etc/nixos/configuration.nix:
{ config, pkgs, ... }:
{
imports = [
/path/to/game/modules/default-deployment.nix
];
services.game = {
enable = true;
apiSecret = "your-secure-api-key-here";
postgresql.enable = true;
ingestionService.enable = true;
ingestionService.port = 8000;
gameService.enable = true;
gameService.port = 8001;
enableMetrics = true;
};
# Allow ports through firewall
networking.firewall.allowedTCPPorts = [ 8000 8001 5432 ];
}
Then deploy:
sudo nixos-rebuild switch
Services will start automatically and be available at:
- Ingestion:
http://localhost:8000 - Game:
http://localhost:8001 - PostgreSQL:
localhost:5432
Option 2: Local Development (Any Linux)
Enter development shell:
nix develop
Start PostgreSQL (in terminal 1):
# Initialize data directory
export PGDATA=/tmp/postgres_data
mkdir -p $PGDATA
initdb $PGDATA
# Start server
postgres -D $PGDATA
In another terminal, initialize the database:
createdb game
psql game < modules/database/01-init_schema.sql
Start Ingestion Service (terminal 2):
export DATABASE_URL="postgres://localhost/game"
export API_SECRET="dev-secret"
export HOST="127.0.0.1"
export PORT="8000"
./result/bin/ingestion-service
Start Game Service (terminal 3):
export DATABASE_URL="postgres://localhost/game"
export API_SECRET="dev-secret"
export HOST="127.0.0.1"
export PORT="8001"
./result/bin/game-service
API Examples
All endpoints require: Authorization: Bearer {API_SECRET}
Submit Sensor Reading
curl -X POST http://localhost:8000/public/events \
-H "Authorization: Bearer dev-secret" \
-H "Content-Type: application/json" \
-d '{
"submissionId": "550e8400-e29b-41d4-a716-446655440000",
"deviceId": "phone-001",
"capturedAt": 1721046000000,
"location": {
"latitude": 37.7749,
"longitude": -122.4194,
"accuracy": 10,
"bearing": 180,
"speed": 5
},
"bluetooth": [],
"wifi": [],
"cellTowers": []
}'
Create Game
curl -X POST http://localhost:8001/games \
-H "Authorization: Bearer dev-secret" \
-H "Content-Type: application/json" \
-d '{"title": "Downtown Hunt"}'
# Response:
# {
# "id": "550e8400-e29b-41d4-a716-446655440000",
# "status": "pending",
# "title": "Downtown Hunt",
# "player_count": 0,
# "created_at": "2026-07-12T10:00:00+00:00"
# }
Join Game
GAME_ID="550e8400-e29b-41d4-a716-446655440000"
curl -X POST http://localhost:8001/games/$GAME_ID/join \
-H "Authorization: Bearer dev-secret" \
-H "Content-Type: application/json" \
-d '{"deviceId": "phone-001"}'
Get Game State
curl http://localhost:8001/games/$GAME_ID \
-H "Authorization: Bearer dev-secret"
List Players
curl http://localhost:8001/games/$GAME_ID/players \
-H "Authorization: Bearer dev-secret"
Database Schema
Core Tables
-
sensor_readings- Raw sensor data from mobile devices- Indexed by: device_id, location (GIST), timestamp
- Includes: GPS, Bluetooth, WiFi, cellular data
-
games- Game sessions- Status: pending, active, completed
- Time tracking: created_at, started_at, ended_at
-
game_players- Players in a game- Tracks: join time, status, score
- One row per player per game
-
challenges- Location-based tasks- Geometry: point (PostGIS)
- References: game_id
-
challenge_completions- Challenge progress- Tracks: which player completed which challenge
Query Examples
-- Latest position for each device
SELECT device_id, location, latitude, longitude, submitted_at
FROM latest_positions
WHERE submitted_at > NOW() - INTERVAL '5 minutes';
-- Games with player counts
SELECT g.id, g.title, g.status, COUNT(gp.device_id) as players
FROM games g
LEFT JOIN game_players gp ON g.id = gp.game_id
GROUP BY g.id;
-- Players in a specific game
SELECT device_id, score, status
FROM game_players
WHERE game_id = '550e8400-e29b-41d4-a716-446655440000';
-- Sensors submitted in last hour
SELECT COUNT(*) FROM sensor_readings
WHERE submitted_at > NOW() - INTERVAL '1 hour';
Monitoring
Metrics Endpoints
Both services export Prometheus metrics:
curl http://localhost:8000/metrics | grep ingestion
curl http://localhost:8001/metrics | grep game
Key metrics:
ingestion_events_submitted_total- submissionsgame_games_created_total- games createdhttp_requests_total- request counthttp_request_duration_seconds- request latency
Logs
View logs (NixOS):
journalctl -u game-ingestion-service -f
journalctl -u game-game-service -f
journalctl -u postgresql -f
View logs (local dev):
- Check terminal output where services are running
Building & Deployment
Build Services with Nix
# Build ingestion service
nix build .#ingestion-service
# Build game service
nix build .#game-service
# Build both
nix build .#ingestion-service .#game-service
Binaries appear in result/bin/.
Update Flake Lock
nix flake update
Cleanup
# Remove build artifacts (but keep .gitignore working)
rm -rf modules/services/*/target/
rm -rf modules/clients/*/target/
rm -rf result result-*
# Prune Nix store
nix-collect-garbage
Project Structure
.
├── flake.nix # Nix build & deployment config
├── DEPLOYMENT.md # Detailed deployment guide
├── README.md # This file
├── .gitignore # Git ignore rules
│
├── modules/
│ ├── default.nix # Module imports
│ ├── default-deployment.nix # Complete deployment config
│ ├── postgresql/ # PostgreSQL NixOS module
│ │ └── default.nix
│ ├── database/ # SQL migrations
│ │ └── 01-init_schema.sql
│ └── services/
│ ├── ingestion-service/ # Sensor data ingestion
│ │ ├── src/
│ │ ├── Cargo.toml
│ │ ├── Cargo.lock
│ │ ├── default.nix # NixOS module
│ │ └── migrations/
│ └── game-service/ # Game logic & state
│ ├── src/
│ ├── Cargo.toml
│ ├── Cargo.lock
│ └── default.nix # NixOS module
│
├── hosts/
│ ├── example-deployment.nix # Example NixOS config
│ └── derelict/
│
└── android/ # Android app (Kotlin)
├── app/
└── build.gradle.kts
Development
Add New Endpoint
- Create route in
modules/services/game-service/src/web/game_routes.rs - Add database query using SQLx
- Update main.rs to mount the route
- Rebuild:
nix build .#game-service
Modify Database Schema
- Create new SQL migration in appropriate directory
- Ensure it's idempotent (safe to run multiple times)
- Rebuild PostgreSQL module:
sudo nixos-rebuild switch(or re-init locally)
Update Dependencies
Edit Cargo.toml files, regenerate locks:
nix develop --command bash -c "cd modules/services/game-service && cargo update"
git add modules/services/game-service/Cargo.lock
Troubleshooting
Database connection refused
# Check PostgreSQL is running
pg_isready -h localhost -p 5432
# Check DATABASE_URL
echo $DATABASE_URL
# Verify database exists
psql -l | grep game
Port already in use
# Find process using port 8000
lsof -i :8000
# Kill if needed
kill -9 <PID>
401 Unauthorized on API calls
- Check
Authorization: Bearer {secret}header is present - Verify API_SECRET env var matches on both client and server
- Example:
curl -H "Authorization: Bearer dev-secret" http://localhost:8000/healthz
Build failures
# Update Nix
nix flake update
# Clean rebuild
nix build --no-cache .#ingestion-service
# Check Rust version
rustc --version
Next Steps
- Build Android client in Kotlin
- Add WebSocket support for realtime updates
- Implement challenge types (waypoints, timers, etc.)
- Add player visibility rules
- Setup TLS/HTTPS with reverse proxy (nginx)
- Add authentication (OAuth, JWT)
- Setup CI/CD pipeline
- Add game history and replays
License
[Add your license here]
Contact
[Add contact info]