No description
  • Rust 99.5%
  • Dockerfile 0.2%
  • Shell 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Tim Janke 55097e4704
All checks were successful
CI / check (push) Successful in 6m34s
CI / publish (push) Successful in 2m11s
📝 (docs): Write down the run no suite can do
Two thirds of the integration gap is automated now: the plugin uploads to a real
server from its own session, and the website can be pointed at one. The last
third needs a passkey and a simulator, so it needs a person — which is a reason
to write it down, not a reason to leave it as an open item that never moves.

ASSEMBLY_RUN.md is that path as a checklist: register, become the administrator,
create an aircraft and an order, pair the plugin, fly it, crash the simulator on
purpose, resume, land, and read the report. It says what to write down
afterwards, because the restore catalogue's dataref names have never been
confirmed against a running aircraft and this is where that happens.

M14 now records what the three bugs were that this run was meant to catch, and
that none of them can recur the same way.
2026-09-07 01:39:15 +01:00
.github/workflows 👷 (ci): Publish the backend image to the Forgejo registry 2026-08-06 07:04:00 +02:00
crates 📝 (docs): Say that the rate limiter is single-process too 2026-09-07 01:27:37 +01:00
docs 📝 (docs): Write down the run no suite can do 2026-09-07 01:39:15 +01:00
migrations 🔒 (devices): Store a signing key, not the device token 2026-09-07 01:17:09 +01:00
proto@10ff156171 ✨ (sim): Verify a chunk as it was sent, then decode it 2026-09-07 01:06:29 +01:00
scripts ✨ (web): Serve the contract this build implements 2026-08-03 08:08:25 +02:00
.dockerignore 👷 (ci): Fail on warnings and on contract drift 2026-08-03 08:08:27 +02:00
.env.example ✨ (meta): Serve the plugin release from a manifest 2026-08-31 07:57:53 +02:00
.gitignore 🎉 (workspace): Add the Cargo workspace and the domain crate 2026-08-03 06:47:52 +02:00
.gitmodules 💚 (ci): Use a relative URL for the proto submodule 2026-08-06 06:42:26 +02:00
Cargo.lock ⬆️ (proto): Track the snapshot and resume schema 2026-09-06 22:13:34 +01:00
Cargo.toml 🎉 (workspace): Add the Cargo workspace and the domain crate 2026-08-03 06:47:52 +02:00
clippy.toml 🎉 (workspace): Add the Cargo workspace and the domain crate 2026-08-03 06:47:52 +02:00
docker-compose.yml 👷 (ci): Fail on warnings and on contract drift 2026-08-03 08:08:27 +02:00
Dockerfile 👷 (ci): Fail on warnings and on contract drift 2026-08-03 08:08:27 +02:00
README.md 📝 (docs): Write down the run no suite can do 2026-09-07 01:39:15 +01:00
rustfmt.toml 🎉 (workspace): Add the Cargo workspace and the domain crate 2026-08-03 06:47:52 +02:00

flyg-backend

The backend for the flyg platform: a virtual-aviation service where pilots rent aircraft, fly orders in X-Plane 12, and earn money for verified flights.

One process serves two contracts.

Surface Path Format Client Credential
Website /api/v1 JSON, RFC 9457 errors, SSE flyg-frontend Session cookie plus a double-submit CSRF header
Simulator /v1 Protobuf over HTTP and WebSocket flyg-plugin-xplane12 Bearer device token

This repository invents neither contract. The website's belongs to flyg-frontend/openapi.yaml, and the simulator's to the flyg-proto schemas. This repository implements both and bridges them: a preflight set on the website reaches the sim, and telemetry from the sim reaches the browser.

The plugin is a sensor and an actuator. Every judgement — whether an order was delivered, whether a recording is trustworthy, what a flight earned — is made here, from what was persisted rather than from what the client asserted.

Running it

cp .env.example .env          # then set DATABASE_URL if you are not using compose
docker compose up -d db       # Postgres on 5433, to stay out of a local one's way
cargo run                     # migrates at startup, then serves

Point the website at it with VITE_API_PROXY=http://localhost:8080 in flyg-frontend.

The whole stack in containers:

docker compose up --build

First run

A migrated database holds seeded airports and nothing else. There are no aircraft and no orders, and both are created through /api/v1/admin, which needs the admin role — a role only an administrator can grant. So the platform would have no way to reach its first order, and the first account to register becomes the administrator. Every account after it is a pilot.

In order, on a fresh deployment:

  1. Register on the website. That account is now the administrator.
  2. Create aircraft (/admin/aircraft) and orders (/admin/orders). Until both exist, the market and the order board are empty for every pilot.
  3. Pair a simulator: the plugin shows a code, and /pair on the website approves it. FLYG_PUBLIC_ORIGIN is what the plugin is told to send the pilot to, so it has to be the website's own origin.

The bootstrap fires only while the users table is empty, so it cannot hand the role to a latecomer. A database that already has pilots but no administrator is the one case it cannot help with, and is corrected directly:

psql "$DATABASE_URL" -c "UPDATE users SET roles = ARRAY['pilot','admin'] WHERE display_name = 'Nordwind'"

Layout

Crate Holds
flyg-core Domain rules with no IO: payouts, verdicts, flight summaries, readiness, track thinning, chunk signatures, cursors
flyg-proto Types generated from the proto/ submodule by prost
flyg-server The binary: axum routers for both surfaces, sqlx repositories, and the hub that joins them

proto/ is the shared schema repository at ssh://git@git.janke.biz:2222/flyg/proto.git, which the plugin uses too. Clone with submodules, or run git submodule update --init --recursive afterwards. Either way needs an SSH key that git.janke.biz accepts.

The hub lives in memory, so it describes this process alone. A second instance would report only its own simulator connections. Running more than one requires a shared channel first.

Tests

cargo test --workspace

flyg-core is tested on its own. flyg-server starts a real server on an ephemeral port, gives each test its own database, and drives both surfaces over real HTTP and real WebSockets: a test signs chunks the way the plugin does, and the server verifies them the way it will in production.

DATABASE_URL must point at a Postgres that may create databases.

Documents

  • docs/BACKEND_PLAN.md — the architecture, the milestones, and the policy questions still open.
  • docs/openapi.yaml — the web contract, copied from flyg-frontend, served at /api/v1/openapi.yaml. scripts/check-contract.sh catches the two drifting apart.
  • docs/ASSEMBLY_RUN.md — flying one order end to end, from registering to the payout. The part of the system no suite can reach on its own.