Skip to content

calvinmclean/ztg

Repository files navigation

Zero Trust Gaming (ztg)

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!

Overview

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

Features

Secure Dice Rolling

  • 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

Identity & Authentication

  • 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

Games

  • 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

Quick Start

Installation

go install ztg/cmd/ztg@latest

Generate Keys

ztg key generate --name my_key

Building Your Own Server

The 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.

Key Components

  1. Strategy: Implement your game strategy logic
  2. OnGameComplete: Handle game completion events (notifications, logging, etc.)
  3. Configuration: Use CUE to define required server settings

Configuration with CUE

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"
}

Example Implementation

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)
    },
}

Identity Store & Trust Management

ztg now includes persistent identity storage using PostgreSQL. This allows server owners to maintain a trusted identity database and manage trust relationships.

How It Works

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

Running with Storage

# 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 server

Managing Trust Status

List All Identities

ztg identity list --server localhost:50052

Mark Identity as Trusted

ztg trust send \
  --server localhost:50052 \
  --peer localhost:50053 \
  --trusted \
  --key-path <owner_key_path> 

Local vs Remote Player Challenges

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:

  1. Player B runs a local server

  2. 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 peer is the local server's address because this is used in it's challenge request to the remote server (matches --server in the next step)
  1. 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.

Database Migrations

The project uses golang-migrate to manage database schema changes. Migrations are located in the migrations/ directory.

Installation

# Install CLI tool
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest

Running Migrations with golang-migrate

Run Migrations

# Up migrations (apply all pending migrations)
migrate -database "postgres://user:password@localhost:5432/ztg_db?sslmode=disable" \
        -path "migrations" \
        up

Challenging Other Players

Once 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.

About

zero-trust peer-to-peer gaming. Challenge other player server's with your programmed game strategy

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages