Skip to content

dstathis/openswiss

Repository files navigation

OpenSwiss

A web application for running Swiss-system tournaments. Built with Go, PostgreSQL, and server-rendered HTML with htmx.

Features

  • Tournament management — Create and run Swiss-system tournaments with configurable points, rounds, and top cut
  • Player registration — Preregistration with optional decklist submission
  • Live standings — Real-time standings with tiebreakers (opponent match win %, game win %, opponent game win %)
  • Playoff brackets — Top-cut single elimination playoffs
  • OTR export — Export tournament results in Open Tournament Results v1 format
  • REST API — Full API for programmatic tournament management
  • Metrics — Built-in /metrics endpoint with request counts, latency, status codes, and Go runtime stats (admin-only)
  • Mobile-friendly — Responsive design optimized for phone and tablet use

Requirements

  • Go 1.21+
  • Docker (for running PostgreSQL)

Quick Start

# Clone the repository
git clone https://github.com/dstathis/openswiss.git
cd openswiss

# Start PostgreSQL in Docker
docker run -d --name openswiss-db \
  -e POSTGRES_USER=openswiss \
  -e POSTGRES_PASSWORD=openswiss \
  -e POSTGRES_DB=openswiss \
  -p 5432:5432 \
  postgres:18

# Run the server
export DATABASE_URL="postgres://openswiss:openswiss@localhost:5432/openswiss?sslmode=disable"
go run ./cmd/openswiss

The server starts on http://localhost:8080 by default.

Register an account through the web UI, then promote it to admin:

docker exec openswiss-db psql -U openswiss -c \
  "UPDATE users SET roles = '{player,organizer,admin}' WHERE email = 'your@email.com';"

Configuration

All configuration is through environment variables:

Variable Default Description
DATABASE_URL postgres://localhost:5432/openswiss?sslmode=disable PostgreSQL connection string
LISTEN_ADDR :8080 Address and port to listen on
MIGRATIONS_PATH file://migrations Path to migration files
RATE_LIMIT_PER_MIN 60 API rate limit per IP per minute
BASE_URL http://localhost:8080 Public base URL (used in password reset emails)
SMTP_HOST (empty) SMTP server hostname (enables password reset when set with SMTP_FROM)
SMTP_PORT 587 SMTP server port (587 for STARTTLS, 465 for implicit TLS)
SMTP_USER (empty) SMTP username (omit for unauthenticated relay)
SMTP_PASSWORD (empty) SMTP password
SMTP_FROM (empty) Sender email address for outgoing mail
SECURE_COOKIES false Set to true to mark session cookies as Secure (requires HTTPS)

Project Structure

cmd/openswiss/       # Application entry point
internal/
  api/               # REST API handlers
  auth/              # Password hashing, session/API key generation
  db/                # Database access layer
  engine/            # swisstools engine wrapper
  export/            # OTR export
  handlers/          # Web UI handlers
  middleware/         # Auth, rate limiting middleware
  models/            # Domain types
migrations/          # SQL migrations
templates/           # HTML templates
static/              # CSS and static assets

Testing

Run the unit tests (no external services required):

go test ./...

Integration tests

The db and engine packages have integration tests that run against a real PostgreSQL database. These are gated behind a build tag and skipped by default.

The Makefile targets automatically create and tear down a test PostgreSQL container:

# Run integration tests
make test-integration

# Run 5000-player load test
make test-load

REST API

The REST API is available under /api/v1/. Authenticate with a Bearer token (API keys can be created from the user dashboard or via the API).

See SPEC.md for the full API reference.

Deployment

A Makefile provides shortcuts for common tasks. Run make help to see all targets.

Docker Compose (Recommended)

Docker Compose sets up PostgreSQL, OpenSwiss, and Caddy (automatic HTTPS) together. The compose file pulls the pre-built image from Docker Hub, so no build step is needed on the server.

# Local development (self-signed TLS on localhost)
make dev

# Production — set your domain to get a real Let's Encrypt certificate
make deploy DOMAIN=tournaments.example.com

# Tail logs
make dev-logs      # or: make deploy-logs

The DOMAIN environment variable controls the Caddy server name. When set to a public domain, Caddy automatically obtains and renews TLS certificates from Let's Encrypt. When omitted it defaults to localhost with a self-signed cert.

You can pin a specific image version with IMAGE_TAG:

make deploy DOMAIN=tournaments.example.com IMAGE_TAG=v1.2.0

To pass additional OpenSwiss configuration (e.g. SMTP), copy the example environment file and edit it:

cp .env.example .env

Docker Compose reads .env automatically. See .env.example for all available options.

After startup, register an account and promote it to admin:

make promote-admin EMAIL=your@email.com

Building & Pushing Images

make build                  # Build the Docker image (tagged dstathis/openswiss:latest)
make push                   # Build and push to Docker Hub
make push IMAGE_TAG=v1.2.0  # Build and push a specific tag

Docker (Manual)

Build and run with Docker:

docker build -t openswiss .

docker run -d --name openswiss \
  -e DATABASE_URL="postgres://openswiss:openswiss@db:5432/openswiss?sslmode=disable" \
  -e SECURE_COOKIES=true \
  -e BASE_URL="https://tournaments.example.com" \
  -p 8080:8080 \
  openswiss

Reverse Proxy (nginx)

In production, run OpenSwiss behind a reverse proxy that handles TLS termination. Example nginx configuration:

server {
    listen 443 ssl http2;
    server_name tournaments.example.com;

    ssl_certificate     /etc/letsencrypt/live/tournaments.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/tournaments.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name tournaments.example.com;
    return 301 https://$host$request_uri;
}

When running behind a reverse proxy with TLS, set SECURE_COOKIES=true so session cookies are marked Secure.

License

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.

See LICENSE for the full license text.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors