A Go framework for building peer-to-peer (no centralized server) games with cryptographic security guarantees. This project implements zero-trust gaming principles where no participant needs to trust any other participant.
Implement and deploy your own Game Strategies to challenge other players!
ztg provides:
- Cryptographically secure dice rolling with peer-to-peer commitment schemes
- Identity verification using Ed25519 signatures
- Tamper-proof game state through hash chains
- gRPC-based server architecture with mutual authentication
- Uses commitment schemes to prevent cheating in distributed dice rolls
- Cryptographically random secrets with SHA256 hash verification
- Peer-to-peer communication without trusted third parties
- Supports rolling up to 8 dice simultaneously
- Ed25519 key pairs for cryptographic identity
- All RPC calls require valid signatures
- Server signs all responses with private key
- Hash chain verification for game state integrity
- Factor Fight: Mathematical strategy game where players combine dice rolls to reach the target 101 with two pawns
- High Roll: Dice rolling game with distributed randomness
go install ztg/cmd/ztg@latestztg key generate --name my_keyThe best way to get started is by implementing your own server with a custom strategy and game completion handler. See the example/server directory for a complete reference implementation.
- Strategy: Implement your game strategy logic
- OnGameComplete: Handle game completion events (notifications, logging, etc.)
- Configuration: Use CUE to define required server settings
Create a config.cue file based on the schema in config/config.cue:
package config
server: {
port: 8080
log_level: "info"
}
identity: {
server_name: "my-server"
owner_name: "my-user"
owner_public_key_file: ".keys/owner.pub"
private_key_file: ".keys/server.pem"
server_address: "my-server.example.com:443"
}The example server shows:
- Custom strategy implementation using
factorfight.DefaultStrategy - Pushover notifications on game completion
- Proper key management and server setup
ffCfg := ffserver.Config{
Strategy: factorfight.DefaultStrategy,
OnGameComplete: func(win bool, _ factorfight.GameLog) {
// Your custom game completion logic here
log.Printf("Game completed. Win: %v", win)
},
}ztg now includes persistent identity storage using PostgreSQL. This allows server owners to maintain a trusted identity database and manage trust relationships.
The identity store provides:
- Persistent Storage: Identities are stored in PostgreSQL
- Trust Management: Server owners can mark identities as trusted or untrusted
- Automatic Discovery: New challengers are automatically added to the database
- Local Verification: Trusted identities can be verified without HTTP requests
# Start PostgreSQL with Docker Compose
docker-compose up -d postgres
# Run server with PostgreSQL connection
ZTG_DATABASE_URL="postgres://ztg:password@localhost:5432/ztg?sslmode=disable" \
go run cmd/ztg/main.go serverztg identity list --server localhost:50052ztg trust send \
--server localhost:50052 \
--peer localhost:50053 \
--trusted \
--key-path <owner_key_path> The identity store allows a player to register their identity with a remote server, which enables initiating challenges from a server without a remote IP/DNS. This is useful for local servers to challenge others during development.
In the following scenario, Player A has a server running in the cloud (ztg.fly.dev:443) and Player B wants to challenge from their server on localhost:
-
Player B runs a local server
-
Player B registers their identity with Player A's remote server:
ztg identity add \ --server ztg.fly.dev:443 \ --peer localhost:50052 \ --peer-name "Player B Local" \ --owner-name "Player B" \ --public-key [PLAYER_B_PUBLIC_KEY]
- The
peeris the local server's address because this is used in it's challenge request to the remote server (matches--serverin the next step)
- Player B challenges Player A by sending a request to their local server:
ztg challenge send \ --target ztg.fly.dev:443 \ --game ztg.FactorFight.v1 \ --server localhost:50052 \ --key-path <owner_key_path>
This enables efficient zero-trust gaming while maintaining identity persistence across server restarts.
The project uses golang-migrate to manage database schema changes. Migrations are located in the migrations/ directory.
# Install CLI tool
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latestRun Migrations
# Up migrations (apply all pending migrations)
migrate -database "postgres://user:password@localhost:5432/ztg_db?sslmode=disable" \
-path "migrations" \
upOnce your server is deployed, you can challenge other players:
ztg challenge send \
--target ztg.fly.dev:443 \
--game ztg.FactorFight.v1 \
--server ztg.fly.dev:443 \
--key-path <owner_key_path> Replace --server ztg.fly.dev:443 with your deployed server address to challenge me with your implementation.