Run locally
For local development the repository ships a docker-compose.yml that provisions every dependency (PostgreSQL, Keycloak + its database, Superposition, Redis + RedisInsight, and LocalStack for S3/KMS), plus a top-level Makefile that wires the dependencies, the init scripts, the server, the dashboard, and the docs together. The two commands you need are make setup (once) and make run (each session).
All commands below are run from the repository root unless noted.
Prerequisites
- Docker (or Podman) with Compose. The Makefile auto-detects
dockerorpodman. - Rust toolchain (the build uses Rust
1.89) pluscargo-watchfor the hot-reload loop. - Node.js (v22 is used for the bundled frontends) and
npm. - Diesel CLI with the Postgres feature, for running migrations manually:
cargo install diesel_cli --no-default-features --features postgres
- Python 3 with SSL support — the encryption init scripts use it (they auto-install the
cryptographypackage into a local folder if needed). - The AWS CLI is used by the init scripts against LocalStack; the local
awslocal/awscalls run inside containers, so a host AWS CLI is optional.
If you use Nix, the repo is set up to provide the toolchain through a dev shell, which covers Rust, Node, and the Diesel CLI in one place. Otherwise install the tools above individually.
Set up dependencies
make setup copies .env, starts the dependency containers, and installs frontend dependencies. Concretely it runs the env-file, db, superposition, keycloak-db, keycloak, redis, redis-insight, localstack, and node-dependencies targets:
make setup
What happens:
env-file— ifairborne_server/.envdoes not exist, it copiesairborne_server/.env.exampleto.envand appendsairborne_server/.env.docker.extra(which adds the Postgres/Keycloak/LocalStack/Superposition container settings and ports).db/keycloak-db— starts the two Postgres containers and waits for them to accept connections.superposition— starts Superposition and waits for its/healthto return200.keycloak— builds and starts Keycloak (with the realm imported) and waits for it to be healthy.redis/redis-insight— starts the Redis cache (published onREDIS_PORT, default6379) and the optional RedisInsight UI (default5540). The server picks Redis up viaREDIS_URL. You can start them on their own withmake redis/make redis-insight.localstack— starts LocalStack with thekmsands3services.node-dependencies—npm cifor the dashboard and the bundled docs app.
By default setup uses encrypted secrets. To set up in plaintext mode instead:
make setup USE_ENCRYPTED_SECRETS=false
make setup only seeds the .env and brings up containers — it does not itself run the Superposition / Keycloak / LocalStack init scripts. Those run as part of make run (and are also available as make superposition-init, make keycloak-init, make localstack-init).
Run the server
make run is the development loop. It kills any stale server process, ensures the dependency containers are up, runs the init scripts, then starts the dashboard, the docs, and the server under cargo watch:
make run
This runs three things concurrently:
- Dashboard —
npm run devinairborne_dashboard. - Docs — the bundled docs React app build.
- Server —
cargo watchrebuilds and restartsairborne_serverwhenever the source changes.
Before starting the server, make run executes the init scripts, which finalize configuration in .env:
init-superposition.sh— creates the default Superposition org and writesSUPERPOSITION_ORG_ID.init-localstack.sh— creates the local KMS key (alias/my-local-key) and the S3 bucket, and (in encrypted mode) KMS-encrypts the master key and the sensitive variables into.env.init-keycloak.sh— reads the realm's client secrets from Keycloak and writesOIDC_CLIENT_SECRET/AUTH_ADMIN_CLIENT_SECRET(encrypted or plaintext per mode), along with the resolved issuer/token URLs.
The encryption mode used at run time is taken from USE_ENCRYPTED_SECRETS in .env (falling back to the make variable). To run without encryption:
make run USE_ENCRYPTED_SECRETS=false
Migrations
Database schema is managed with Diesel; migrations live in airborne_server/migrations/ and diesel.toml points the generated schema at src/utils/db/schema.rs.
There are two ways migrations run:
- Embedded, on boot — the binary embeds the migrations (
embed_migrations!) and runs any pending ones at startup whenMIGRATIONS_TO_RUN_ON_BOOTincludesdb(the example.envsetsdb,superposition). See boot-time migrations. - Manually, via the CLI — run them yourself with the Makefile target:
This sourcesmake db-migration
airborne_server/.env, constructsDATABASE_URL(preferringDB_URL/DATABASE_URLif present, otherwise assembling it with the default migration password), and runsdiesel migration runfromairborne_server.
You can run Diesel directly from airborne_server once DATABASE_URL is exported, e.g. cd airborne_server && diesel migration run. The make db-migration target just sets that up for you from .env.
Hit the health check
Once the server is up, verify it:
curl http://localhost:8081/api/health
A healthy server returns 200 OK with { "status": "ok" }. The path is /{SERVER_PATH_PREFIX}/health; SERVER_PATH_PREFIX defaults to api. The server port is PORT (default 8081).
You can also sanity-check the dependencies:
# Superposition
curl http://localhost:8080/health
# Keycloak realm
curl http://localhost:8180/realms/master
# LocalStack
curl http://localhost:4566/_localstack/health
make status prints a summary of which containers are running.
Stopping and cleaning up
make stop # stop the dependency containers
make cleanup # stop + remove volumes and delete airborne_server/.env
Troubleshooting
make cleanup removes the database/Keycloak volumes and deletes airborne_server/.env. You will need to run make setup again (and re-run the init scripts via make run) afterwards.
MASTER_KEY must be set when USE_ENCRYPTED_SECRETS=true— you are in encrypted mode but the master key was never generated/encrypted into.env. Runmake run(which executesinit-localstack.sh), or switch to plaintext withUSE_ENCRYPTED_SECRETS=false. The plaintext DEK lives inairborne_server/.masterkey.local; deleting it and.envtogether resets encryption state.AUTH_ADMIN_* must be set when AUTHN_PROVIDER=keycloak— the Keycloak init step did not populate the admin client values. Confirm Keycloak is healthy (curl http://localhost:8180/realms/master) and re-runmake keycloak-init.SUPERPOSITION_ORG_IDlooks wrong /get-org-id-from-superposition— the Superposition init step did not run or could not reach the service. Confirmcurl http://localhost:8080/healthreturns200, then re-runmake superposition-init.- Database connection refused — the Postgres container may still be starting, or the port is taken. The app Postgres is published on
5433(not the default5432); make sure nothing else is bound there and thatDB_PORT=5433. - Port already in use — the defaults are server
8081, Superposition8080, Keycloak8180, Postgres5433, Keycloak DB5434, LocalStack4566, Redis6379, RedisInsight5540. Free the conflicting port or override it in.env. - Encryption scripts fail on Python — they need a Python 3 with SSL and will install
cryptographyintoairborne_server/scripts/.python-tools. Ensurepython3 -c 'import ssl'works.
Next
- Configuration — every environment variable explained.
- Deploy on AWS ECS and Deploy on AWS EKS — taking the same dependency model to production.