Deploy staging
Terminal and this documentation are static Cloudflare Pages sites. Mainframe staging runs Core, Postgres, SRS, the media indexer, Caddy, world-sim, auto-operator, and monitoring on its own Hetzner server. DVR and review objects use a private Hetzner S3 bucket. It uses real credentials and the normal authentication path.
| Surface | Address |
|---|---|
| Terminal | https://terminal.dev.mainline.inc |
| Mainframe | https://mainframe.dev.mainline.inc |
| Documentation | https://docs.terminal.dev.mainline.inc/ |
| Grafana | https://mainframe.dev.mainline.inc/grafana/ |
| Simulator administration | https://mainframe.dev.mainline.inc/sim/admin |
These are the configured targets. DNS and TLS must be provisioned before the addresses will work.
1. Provision Pages, the host, and bucket
Section titled “1. Provision Pages, the host, and bucket”The separate infra repository owns the Git-integrated Pages projects (terraform/terminal-pages.tf), custom domains, Hetzner server, firewall, and object storage (terraform/terminal-staging.tf). Its Terminal staging resources reuse the account, SSH keys, and providers used by the CC clusters while creating a separate server and private bucket.
Review the Terraform plan before applying it. Use the resulting server IP for PUBLIC_IP. Follow capacity planning when choosing server and disk sizes. The server needs Docker with Compose 2.24.4 or newer and Bun 1.3.14 for the deployment launcher.
Public traffic uses TCP 80 and 443 for HTTP/TLS, UDP 8000 for WebRTC, and TCP 1935 for authenticated RTMP publishing. SSH access should match the administrator CIDRs configured in Terraform. Postgres, SRS’s API, Prometheus, and exporters are not public services.
Static builds
Section titled “Static builds”Both Pages projects use the repository root, Bun 1.3.14, SKIP_DEPENDENCY_INSTALL=1, and the main production branch. Every push to main rebuilds and publishes both sites without touching Docker. Pull requests receive separate preview builds.
| Project | Build command | Output |
|---|---|---|
terminal-staging |
bun install --frozen-lockfile && bun run --cwd apps/web build |
apps/web/dist |
terminal-docs-staging |
bun install --frozen-lockfile && bun run --cwd docs build |
docs/dist |
Terminal’s build environment sets VITE_MAINFRAME_URL=https://mainframe.dev.mainline.inc. Mainframe continues to allow the Terminal origin through its configured CORS/auth settings; preview origins are not automatically trusted for staging login. The old Terminal /docs/ URLs redirect to the documentation domain.
The Hetzner console name is mainframe-staging. Its Terraform resource address, /opt/terminal checkout, terminal-staging Compose project, and existing volumes retain their original identities. Renaming the console label does not change the public IP or DNS.
2. Prepare the backend checkout
Section titled “2. Prepare the backend checkout”Place the reviewed Terminal checkout on the server, then run these commands from its root:
bun install --frozen-lockfilebun run deploy initThis creates deploy/.env.staging with independent random credentials and mode 0600. It refuses to overwrite an existing environment. Set the server IP and S3 credentials, and review the account emails and domains. Keep this file outside version control.
The bucket endpoint and region must match, for example https://hel1.your-objectstorage.com and hel1. The bucket must already exist. Leave BLOB_PRESIGN=0 to serve authenticated playback through Mainframe without configuring public bucket CORS.
3. Validate and launch
Section titled “3. Validate and launch”bun run deploy configbun run deploy upbun run deploy psThe launcher combines the base, monitoring, and staging Compose files. It validates required fields, enables the selected Core module identities, generates Prometheus scrape targets, builds backend images, and waits for service health checks. Caddy uses the stock image and serves only Mainframe, module routes, and monitoring; it does not build Terminal or docs. A normal rebuild preserves named volumes.
If local map assets are required, build them before deployment using the repository’s map tooling. The stack can otherwise use the frontend’s configured online basemap.
4. Seed the virtual fleet
Section titled “4. Seed the virtual fleet”bun run deploy seedbun run deploy seed --applyThe first command shows missing records. The second creates them through Mainframe’s authenticated document and fleet APIs. It adds ten SIM-STG-001 through SIM-STG-010 aircraft, one staging flight, four short patrol loops, areas, and non-dispatching watch rules. Re-running preserves existing documents and fleet identities.
Auto-operator runs in auto mode with AUTO_OPERATOR_SERIAL_PREFIX=SIM-STG- and AUTO_OPERATOR_PROVIDER_IDS=world-sim. Mainframe independently checks the same selection. Real aircraft from another provider cannot match it. The aircraft start at different battery levels; four missions leave six aircraft available for charging and rotation.
5. Verify the deployment
Section titled “5. Verify the deployment”Sign in to Terminal with the admin credentials in the environment file. Confirm the virtual fleet appears, missions become assigned, telemetry advances, and a live feed plays. After at least one DVR segment closes, enter replay and confirm video and positions match the selected time.
In Grafana, inspect service availability, module connections, S3 uploads, simulator publishers, automation freshness, and mission coverage. Check an actual charge → dispatch → return → recharge cycle before calling the continuous run accepted. A long replay history requires a real soak run; an initial healthy boot proves only that the stack started.
Use updates and backups for subsequent releases and real aircraft before enabling the Autel profile.