A modern web application for managing orienteering competitions — from entry and course setup through start draw, SI card readout, and live results. Built by Open Orienteering. Inspired by MeOS, rebuilt as a Progressive Web App with direct Eventor integration.
Note (May 2026): Oxygen previously stored data in a MeOS-compatible MySQL layout. That compatibility layer was dropped in May 2026 — the app now runs on a single PostgreSQL 18 database (
oxygenschema). Seedocs/migrations/2026-drop-meos.mdfor the migration story and the one-shot CLI that moves a MeOS database into the new schema.
- Competition management — create and manage events, races, and classes
- Eventor integration — sync entries, competitors, and clubs directly from the Swedish Orienteering Federation's API; upload results and start lists back
- Course management — import courses from OCAD/IOF XML, manage controls and legs
- Start draw — automated start time allocation with configurable algorithms
- SI card readout — read SportIdent cards via Web Serial, process punches and compute results live
- Live results — real-time result updates as cards are read
- Kiosk mode — self-service registration and start/finish station interfaces
- PostgreSQL 18 — single-database design with UUIDv7 PKs, JSONB columns, native enums, and foreign-key integrity
See the feature showcase for screenshots of every view.
| Layer | Technologies |
|---|---|
| Frontend | React 19, Vite 8, Tailwind CSS v4, TanStack Query v5, React Router v7 |
| Backend | Fastify 5, tRPC 11 (end-to-end type safety), Zod 4 |
| Database | PostgreSQL 18 (oxygen schema), Prisma 7 ORM |
| Testing | Playwright 1.62 E2E, Vitest 4 unit tests |
oxygen/
packages/
api/ # Fastify + tRPC backend
web/ # React PWA frontend
shared/ # Shared types and utilities
e2e/ # Playwright E2E tests
docs/ # Guides and tutorials
For a deeper dive into database design, deployment options, and subsystem details, see the technical architecture.
Pre-built multi-arch images live on GHCR (ghcr.io/open-orienteering/oxygen).
See releases and images for tags (stable,
edge, vX.Y.Z) and how GitHub Releases are cut.
docker compose -f docker-compose.release.yml up -d
# app: http://localhost:8080Pin a release with OXYGEN_IMAGE=ghcr.io/open-orienteering/oxygen:v1.2.3.
git clone https://github.com/open-orienteering/oxygen
cd oxygen
cp packages/api/.env.example packages/api/.env
docker compose up -d postgres # start PostgreSQL 18
pnpm install
pnpm db:push # apply the oxygen schema
pnpm db:generate
pnpm dev # API on :3002, web on :5173Prerequisites: Node.js >= 20, pnpm >= 10, PostgreSQL 18
pnpm install
# Edit packages/api/.env so DATABASE_URL points at your PostgreSQL,
# e.g. postgresql://oxygen:oxygen@localhost:5432/oxygen?schema=oxygen
pnpm db:push
pnpm db:generate
pnpm devSee packages/api/.env.example for all environment variables.
Click the Open in Cloud Shell button at the top of this page to pull the published GHCR image and spin up a fully working instance in your browser — no Node.js, pnpm, dependency install, or source build needed. The Cloud Shell tutorial walks you through loading demo data or connecting your Eventor API key.
If you have access to the Swedish Orienteering Federation's Eventor system, you can connect Oxygen directly:
- Log in to eventor.orientering.se → your profile → API key
- Open Oxygen → Settings → paste the key
This syncs entries, clubs, and competitors automatically and lets you upload results/start lists back to whichever Eventor the event is linked to — Test-Eventor or production.
GNU Affero General Public License v3.0 — free to use and modify; derivatives must remain open source, including when deployed as a hosted service.