Newer VATUSA backend API, intended to extend/replace the legacy Laravel API (api). It's the
sole identity provider for the VATUSA stack — webapps (the modern frontend) and current
(the main website) both delegate login to it.
- Go 1.25, HTTP via Echo v5
- MySQL, accessed through sqlc-generated code (
src/db, generated fromsql/queries+sql/migrations+sql/legacy_schema) — never hand-editsrc/db - Migrations applied with golang-migrate
- JWT sessions via
golang-jwt - VATSIM Connect (OAuth2) for login, plus VATSIM API v2 for roster/rating data
k8s.io/client-gofor cluster access from within the app (e.g. background jobs)
Three binaries are built from src/cmd/: server (HTTP API), background (async worker),
cli (one-off tasks). The Dockerfile builds all three; its entrypoint defaults to server.
cp .env.example .env # fill in DB credentials, JWT_KEY, etc.
cd src
go build ./...
go run ./cmd/server/Run tests with go test ./... from src/.
If you change a query in sql/queries or the schema, regenerate the DB layer from the repo
root:
sqlc generate| Package | Purpose |
|---|---|
endpoints, routes, middleware |
HTTP layer |
auth, login, acl |
Authentication (JWT) and role/permission-based access control |
db, dbconn, models |
Data access (db is sqlc-generated) |
action, roster, background |
Domain logic and async work |
vatsim |
VATSIM API/Connect integration |
moodle |
Moodle integration (e.g. rating cohort sync) |
legacy_migration |
Migration helpers from the legacy (VATUSA-old) system |
config, cli, server, cmd |
Wiring and entrypoints |
All routes are registered in src/routes/routes.go.
| Group | Endpoints | Purpose |
|---|---|---|
/token, /tokenSession |
GET /token/:cid, POST /tokenSession |
Actor-authenticated token issuance/lookup (server-to-server) |
/news |
latest/page/post CRUD | News posts |
/roles |
POST /legacy_sync[/bulk] |
Sync roles from the legacy system |
/event |
upcoming/page/CRUD/review | Events |
/user/:cid |
GET, GET /blockers |
User lookup |
/login |
see below | VATSIM Connect login flow |
/my |
GET /session, POST /transfer |
Current-user session info, facility transfer requests |
/roster/:facility |
roster + pending transfers | Facility roster management |
/facility/:facility |
GET /v3/apikeys |
Facility API key/tech config |
Two independent auth mechanisms are layered on every request (routes.SetupRoutes):
- Actor auth (
middleware.ActorAuth/auth.ActorTokenHeader) — a static bearer token presented by trusted server-to-server callers (e.g.current, or a relaying instance of cobalt itself). Resolves to an actor ID cached from the DB and refreshed every minute. - Cookie auth (
middleware.CookieAuth) — reads the JWT session cookie (auth.JWTCookieName) set at login and resolves it to a user CID.
Handlers that require a logged-in user wrap with middleware.RequireLogin. Authorization on
top of that is role/permission-based (acl package): roles are scoped either globally or to a
facility, and acl.PermissionHandler checks an actor's or user's cached roles against
(object, action) or (facility, object, action) permission keys.
Cobalt owns the VATSIM Connect OAuth registration, and VATSIM only whitelists a
redirect_uri on cobalt.vatusa.net — so every non-prod (dev/staging) login has to proxy
through the prod instance:
GET /login— on non-prod, redirects to prod's/login/staging(VATSIM would reject a non-prod redirect_uri).GET /login/staging(prod-only) — logs into prod via VATSIM Connect if needed, then calls back to the originating dev/staging instance's/token/:cid(actor-token gated) to mint a token, and redirects the browser to that instance's/login/useToken/:token.GET /login/connect— VATSIM's OAuth callback; sets the session cookie. If prod is mid-relay for a dev/staging login, loops back into/login/stagingto complete the handoff instead of landing onPOST_LOGIN_URL.GET /login/useToken/:token(non-prod only) — sets the session cookie from the relayed token.
A caller-supplied redirect query param (validated against REDIRECT_ALLOWLIST via
config.IsAllowedRedirect) lets consumers like current request their own callback instead
of the default POST_LOGIN_URL; it's carried across the VATSIM round trip via the OAuth
state param. A _staging_relay|-prefixed state is how Connect() distinguishes a relayed
dev/staging login from an ordinary prod login.
See CLAUDE.md for more implementation detail on this flow.
A Bruno collection lives in Bruno/ for exercising endpoints
locally (api, login, roster, user, events, news, etc.).
See LICENSE.md.
Contributions are currently closed to anyone not on the VATUSA Web Team.