Skip to content

Onboarding

What otomo is

Otomo is a game-agnostic backend framework for online games. It provides the identity, content delivery, session, administration and edge pieces a multiplayer game needs: a set of Go 1.27.1 services, a staff admin UI, an allocator for game servers, and a Docker Compose deployment that runs on one Linux host. Otomo contains no game content and depends on no particular game. Your game plugs into its HTTP contracts and supplies its own client and game servers.

Architecture

                          Internet
                             │
                             ▼
                  Edge (nginx :80/:443)
        TLS, ACME; /docs/ → technical wiki,
        / → game site, player API → gateway
                             │
        ┌────────────────────┴────────────────────┐
        ▼                                          ▼
  Godot client                              Ionic admin UI
     (player)                                   (staff)
        │                                          │
        ▼                                          ▼
Gateway (player edge, public)          Gateway (dev, admin edge, private)
        │                                          │
        └───────────────────┬──────────────────────┘
                            ▼
                   Essential services
        Go 1.27.1: Auth, Config, Patch, Session,
        Dashboard, admin-auth
                            │
              ┌─────────────┴─────────────┐
              ▼                           ▼
         Allocator                    Data layer
   (internal only; reserves     Postgres, Valkey,
    servers, issues tickets)    blob storage
              ▲
              │ register + heartbeat
        Game servers (headless)
              │
              ▼
        Gameplay Proxy (public UDP 27000)

The Gameplay Proxy carries player connections to the game servers; clients never reach a game server directly. See the launch hand-off for the launch path.

Repository layout

Path What is in it
services/ One directory per service. A Go service holds its module, Dockerfile and migrations/; the admin UI and wiki have their own build layouts.
deploy/ compose.yaml, the provisioning and release scripts, the Postgres init script, the edge runbook, observability configs and generated secrets.
ci/ Jenkins pipeline files: ci/services/Jenkinsfile, one unit_test.sh per service, the dispatcher, and the CI Compose stack.
docs/ The source for this technical wiki, including docs/guide/.
.github/workflows/ci.yml GitHub Actions workflows for gateway, gateway_dev and adminui.

Two more directories sit under services/: matchmaker, still a placeholder that is not in the running stack, and cli.

Services

Service What it does Design doc
edge nginx on :80/:443; TLS termination, ACME, routes the player API to gateway, /docs/ to the technical wiki and / to the game site Allocator and edge plan, wiki service
gateway Player edge: routing, token validation, per-IP rate limiting Gateway techspec
gateway_dev Staff/admin edge: the same job for the private control plane Gateway techspec, common stack
auth Player identity: device login, refresh rotation, JWKS Auth identity contract, Auth techspec
admin_auth Staff identity: login, MFA, refresh cookies, user administration, JWKS Auth identity contract
config Author, validate, version and publish config and content Config
patch Serve published manifests and content blobs to clients Patch (minimal)
session Profiles, presence, friends, parties (lobbies) and events Session (minimal)
allocator Internal only; reserves a game server and issues single-use join tickets Allocator and edge plan, launch hand-off
dashboard Staff metrics, logs and audit trail Dashboard
adminui Ionic staff SPA hosting the Config and Dashboard modules at /admin/ Common stack
wiki MkDocs Material builder and runtime for the technical wiki and a game site Wiki service

How a change flows

  1. Branch off staging, named SCRUM-<n>-short-description.
  2. Commit and open a pull request into staging.
  3. CI runs:
  4. GitHub Actions (.github/workflows/ci.yml) runs on any push or pull request that touches services/gateway/, services/gateway_dev/ or services/adminui/.
  5. Jenkins runs the per-service jobs (otomo-<service>), all driving ci/services/Jenkinsfile. Jenkins is CI for staging only and no longer deploys.
  6. Merge into staging.
  7. Deploy manually, one service at a time, by image tag:
deploy/scripts/release.sh auth                 # tag = short git SHA of the working tree
deploy/scripts/release.sh gateway 1.4.0        # or an explicit tag

release.sh builds 127.0.0.1:5000/otomo-<service>:<tag>, pushes it, writes that tag into deploy/.env and recreates the service. For a service with a *-migrate one-shot (for example auth), the recreate also runs its migration.

Rolling back is a re-point, not a rebuild:

sed -i 's/^OTOMO_AUTH_TAG=.*/OTOMO_AUTH_TAG=<previous sha>/' deploy/.env
cd deploy && docker compose --env-file .env -f compose.yaml up -d auth

Tags are immutable and the registry is not pruned, so the previous image is still there. A rollback of a service with migrations needs a schema that is compatible in both directions, because goose does not undo a version on its own.

Local development

  • The toolchain is Go 1.27.1, pinned exactly. Every go.mod carries go 1.27.1 and toolchain go1.27.1; build and test with that version.
  • Run a service's tests from its directory:
cd services/<service>
go test ./...          # add -race as CI does
  • DB-backed tests need the CI database variables, named <SERVICE>_TEST_DATABASE_URL (for example SESSION_TEST_DATABASE_URL). CI provides Postgres and Valkey in ci/compose.yaml.
  • Run one CI test level locally the way Jenkins does:
docker compose -f ci/compose.yaml run --rm --workdir /src go \
  sh ci/services/otomo-<service>/unit_test.sh sanity
docker compose -f ci/compose.yaml down -v

Use node instead of go for adminui, and add --no-deps for a service that needs no database. See ci/services/README.md.

Conventions

Area Convention
Error body {"error":{"code":"...","message":"...","request_id":"..."}} for every 4xx and 5xx (COM-5)
Request IDs Gateway adds X-Request-Id when absent; every service logs it and forwards it on outbound calls (COM-3)
Health endpoints /healthz, /readyz and /metrics are served on the internal :9090 listener, never on the public port
Images Multi-stage builds, runtime image gcr.io/distroless/static-debian12:nonroot; non-root, no shell, so no Compose healthchecks and nothing to docker compose exec
Secrets Never in the repository; generated into deploy/.env and deploy/secrets/ at deploy time (COM-9)
Logging log/slog JSON on stdout
Metrics Names <service>_<thing>_<unit>; no player IDs, staff IDs, raw URLs or request IDs as labels

A 503 from either gateway's /readyz means one token domain's JWKS is not fetching, not that the stack is down. Both gateways wait on two domains.

Security rules

  • No otomo cookie sets Domain. Login-session cookies use the __Host- prefix. The public edge strips Cookie and Set-Cookie on every public route, and HSTS is sent for this host only, never with includeSubDomains or preload. The parent domain is shared with other teams. See the auth identity contract.
  • The Let's Encrypt quota is shared. The certificate authority issues at most 50 new certificates per week for the whole parent domain. Rehearse with dry-run (staging CA, no quota) every time and request the real certificate exactly once per host name. The edge container never requests certificates itself. See the Allocator and edge plan and deploy/edge/README.md.
  • No internal route is reachable through either gateway. Internal calls carry a per-caller service key, checked on the callee's internal listener only. Never trust X-Forwarded-For unless the peer is in the gateway's configured trusted-proxy list.

Where to go next

If you want to Read
Understand the whole M1 stack M1 overview, common stack
Work on routing, rate limits or the edge Gateway techspec, Allocator and edge plan
Work on identity Auth identity contract, Auth techspec
Work on content Config, Patch (minimal)
Work on sessions and lobbies Session (minimal), player plane plan
Work on game-server launch Launch hand-off
Build the game client Godot SDK guide
Run or edit a wiki or site Wikis and sites, wiki service
Deploy otomo for your game Setting up otomo for your game