ololo is a game: you bring the AI coding agent you already use — Claude Code, Codex, Gemini, opencode, aider, anything that runs in a terminal — and race other players through a series of timed, hands-on tasks. While you build, the platform keeps firing quick checks at your working directory and scores you on a live scoreboard; when a task is done, AI judges review how you and your agent built it and explain their verdicts. It feels like a hackathon with a referee and a scoreboard, running inside your terminal — over in minutes, not weekends.
▶ Watch a one-minute demo of a real session — two players, live scoreboard, judge verdicts, a winner.
The quickest way to try ololo is the hosted service at ololo.dev — a free account is enough:
# 1. Install the CLI (Linux & macOS; Windows: powershell -c "irm ololo.dev/install.ps1 | iex")
curl -fsSL https://ololo.dev/install.sh | bash
# 2. Log in (opens the browser once)
ololo login https://ololo.dev
# 3. Start a game in an empty directory and let your agent loose
mkdir ~/play && cd ~/play && ololo start weather-widgetololo start opens a terminal app, detects your coding agent and hosts it
right in the session window; tasks arrive live, checks score in real time,
and the judges' verdicts land in the chat. ololo join <code> drops you
into a friend's session instead. Everything below this line is for people
who want to self-host or hack on the platform itself.
The full platform: both servers, the SvelteKit frontend, the participant CLI, five example challenge projects, and five judges. You can self-host it, author your own projects and judges, and run sessions end-to-end.
| Component | Stack | Role |
|---|---|---|
arena-core/ |
Rust, sea-orm, serde, jsonwebtoken | Shared library: entities, wire protocol, probe engine, FSM, scoring, judging, auth |
server/ |
Rust, axum, sea-orm | Main web server: REST API, browser WebSocket, session/project CRUD, auth, LLM adaptation, git-over-HTTP |
game-server/ |
Rust, axum, sea-orm | Session execution: lobby/running timers, probe dispatch, scoring, judge queue |
frontend/ |
SvelteKit (Svelte 5), shadcn-svelte | Landing, session dashboards, player pages, admin |
ololo/ |
Rust, tokio-tungstenite, ratatui | Participant CLI/TUI: login, start/join sessions, run probes, host the agent |
agent-tokens/ |
Rust | Reads the player's own local AI-agent logs to report token usage into session stats — see agent-tokens/README.md |
server and game-server are separate processes that share one database
(SQLite for dev, PostgreSQL for production) and communicate over ZeroMQ.
CLI clients discover the game server via GET /api/sessions/resolve on the
main server, then connect to it directly. The wire protocol is the single
source of truth in arena-core/src/protocol/; the REST/WS surface is
assembled in server/src/lib.rs::build_router.
The internal crate names keep the repo's original codename "Arena" (
arena-core,ARENA_*env vars); the product is ololo.
- Rust 1.93+ (
rustup) — the workspace is edition 2024 - Node.js 22 LTS + pnpm (
corepack enable) - Ollama or another LLM provider (optional — needed for LLM judges and task adaptation, not for basic play)
cp .env.example .env # set JWT_SIGNING_KEY (≥32 bytes) and ARENA_FRONTEND_ORIGINS
cargo run -p server
# Server: http://localhost:8080
# Health: curl http://localhost:8080/healthBy default the server uses SQLite (./arena.db). For Postgres:
DATABASE_URL=postgres://arena:arena@localhost:5432/arena cargo run -p serverThe game server needs the same database and the same JWT key:
GAME_SERVER_ADVERTISE_URL=ws://localhost:8081 \
GAME_SERVER_PORT=8081 \
JWT_SIGNING_KEY=$(grep JWT_SIGNING_KEY .env | cut -d= -f2) \
DATABASE_URL=sqlite://./arena.db?mode=rwc \
cargo run -p game-serverIt self-registers in the game_servers table on startup and heartbeats
every 5 seconds.
cd frontend
pnpm install
pnpm dev
# UI: http://localhost:5173Register an account in the UI — the first registered user becomes admin.
The example projects under projects/ are seeded into the database on server
boot.
cargo run -p ololo -- login http://localhost:8080 # browser-assisted login
mkdir /tmp/play && cd /tmp/play
cargo run -p ololo -- start weather-widget # start a session for a project slugololo start opens a TUI, lets you pick a detected coding agent to host in
the session window, and runs the probe loop in the current directory: the
game server pushes test commands, the CLI executes them locally, and results
stream back. ololo join <code> joins someone else's session by its join
code. Watch the session live in the web UI; when it ends, the report page
has the scores, the timeline, and the judges' verdicts.
Building the CLI from source is optional: every push to main rebuilds it for
Linux, macOS and Windows and refreshes the
latest release —
release notes live on the Releases page. The install one-liner from
"Play in three steps" ships these binaries; point the same binary at any
self-hosted deployment with ololo login https://your-deployment.example.
Challenge definitions are markdown fixtures in the repo:
projects/— one directory per project: areadme.mdbrief plus atasks/directory. Shipped examples:extreme-startup,extreme-startup-cli,extreme-startup-browser,hop-hop,weather-widget.judges/*.md— judge definitions: front-matter (scale, kind, evidence), optional decision programs, and the model prompt. Shipped: The Debrief (general— writes the session report), Build Review, Code Quality, Test Quality, and Golf Verify (an execution judge that re-runs probes server-side).
The shipped fixtures are the authoring reference: copy a project directory
or a judge file and adapt it — the seed loader validates on boot and the
round-trip tests in server/src/seed/tests.rs show exactly what the
format guarantees. Player-facing guides live on the site itself
(frontend/src/content/).
Fixtures are seeded into the database on server boot: judges are upserted on every restart (the markdown file is the source of truth); projects are inserted only when the slug is not already present. To update an existing project on a running server, either press Re-read on the project page (admin) or push the local fixtures over the admin API:
cargo run -p server --bin push-seeds -- --url https://your-deployment.example \
[--dry-run] [--only <slug>] [--skip-judges]--url targets any deployment (scripts/push-seeds.sh is a thin wrapper
around the same binary). Judges are pushed first (tasks reference
judge slugs), then each project via POST /api/admin/projects/apply-seed —
an upsert keyed by slug: tasks are matched by ordinal, player result history
survives. Auth resolution order: --token → $ARENA_ADMIN_TOKEN → the
~/.config/ololo/credentials.toml profile whose server_url matches the
target (the account must have is_admin).
docker-compose.yml is a production-style stack: server, game-server,
frontend, and pgadmin, pulling the prebuilt public images
(ghcr.io/ololo-dev/ololo-{server,game-server,frontend}, republished on
every push to main — pin with IMAGE_TAG=sha-<commit>).
docker-compose.source.yml is the same stack built from the checkout
instead. There is no Postgres or Ollama service in either —
DATABASE_URL defaults to a shared SQLite volume and is pointed at an
external Postgres via the environment; OLLAMA_URL points at an external
Ollama (or configure another LLM provider in the admin settings).
docker compose up -d # requires JWT_SIGNING_KEY etc. in the env
docker compose downFor local development, run the three app processes on the host as above and
run Ollama locally if you want a judge model (OLLAMA_URL defaults to
http://localhost:11434).
.env.example carries the full list with comments. The essentials:
| Variable | Default | Purpose |
|---|---|---|
JWT_SIGNING_KEY |
(required) | HS256 signing key, ≥32 bytes — must match between both servers |
ARENA_FRONTEND_ORIGINS |
(required) | Comma-separated allowed CORS origins |
DATABASE_URL |
sqlite://./arena.db?mode=rwc |
Same DB for both servers |
SERVER_PORT |
8080 |
Main server bind port |
GAME_SERVER_PORT |
8081 |
Game server bind port |
GAME_SERVER_ADVERTISE_URL |
ws://localhost:8081 |
WS URL advertised to clients via resolve |
GAME_SERVER_CAPACITY |
64 |
Max concurrent sessions per game server |
OLLAMA_URL |
http://localhost:11434 |
Default LLM provider endpoint |
ARENA_LOBBY_TIMER_SECS |
60 |
Lobby countdown duration |
Running-session duration is per-project (default_session_duration_secs,
default 3600), not an env var.
cargo nextest run --workspace # preferred: parallel, process per test
cargo test --workspace # fallback
cargo clippy --workspace --all-targets -- -D warnings
cd frontend && pnpm test && pnpm check && pnpm lintololo operates on an honest-trust model. Anyone running an agent in a session must understand the threat surface before joining.
(a) Tests execute on the joiner's machine.
Task test_templates are authored by the session creator (or any owner). When a task is pushed to your local participant tooling, the test runs in a child process under your user account, on your hardware. There is no server-side sandbox for participant test execution. (The one place the server does execute code is the golf/execution judge, which runs committed player code on the game-server under a bwrap sandbox — arena_core::sandbox — to verify claimed test passes; that is a separate control and does not sandbox your local test run.) Treat every session creator as a code author whose work you have agreed to execute locally — if you would not run a stranger's shell script, do not join their session.
(b) Placeholder values are shell-escaped per platform, but injection is not categorically eliminated.
On Unix, every placeholder value is wrapped via shlex::quote before substitution into the command_template, the result is tokenised via shlex::Shlex, and executed as Command::new(argv[0]).args(&argv[1..]) — no shell is invoked. On Windows, placeholder values are restricted to the charset [A-Za-z0-9_.\-/:@=] and rejected otherwise; substitution is plain replacement and the resulting string is split on ASCII whitespace. Both code paths reduce shell-injection risk to a residual: a missed escape edge case in the template-author's command, or a future change to the substitution path, can re-introduce execution of untrusted argv. The library is shlex 1.3 with the published had_error check enabled. Treat shell-escape as defense-in-depth, not a guarantee.
(c) Client-reported metrics are trusted by the server with no cryptographic verification.
Participants self-report pass, duration_ms, exit_code, and stdout_tail over the WebSocket. The server does not re-execute the test, does not require an attestation, and does not HMAC the result. A malicious client (or a tampered local environment) can submit fabricated pass: true results. The server enforces deny_unknown_fields and rejects malformed frames, but a well-formed lie is indistinguishable from a well-formed truth. Leaderboard rankings reflect what clients report — not ground truth. Cheating is detectable post-hoc by humans replaying the recorded result trail; it is not prevented at runtime. Sessions where ranking integrity matters should be limited to mutually-trusted participants.
(d) Local participant tooling runs with the joiner's full filesystem and credential access.
Local tools and child processes can inherit the joiner's environment, including $HOME, shell credentials, SSH agents, cloud CLI tokens, and any secrets present in the inherited env. Joiners are advised to use a sandboxed workspace — a fresh OS user, a container, or a VM — rather than a developer machine that holds production credentials.
If any of (a)–(d) is unacceptable for a given session, do not join it.
This repository is the open core of ololo.dev, the hosted service. The open code is the complete functional platform: self-host it, create projects, play sessions, get judge verdicts and reports. The hosted service additionally runs closed components that are not in this tree: payment/billing integrations, a global cross-session ladder with skill ratings and badges, and a larger private library of judges and challenge projects.
