ados-deploy is a Rust terminal tool in the Mission Control repo at
tools/deploy/. It brings up the self-hosted stack (Convex backend and
dashboard, the Mission Control web GCS, Mosquitto with its auth sync, and the
video relay) through a guided flow, then keeps the lifecycle commands in one
place.
It is not a second stack. It drives the same Compose project documented on
All-in-one stack,
tools/selfhost/docker-compose.yml. It adds the ordering, the generated
config, the secret handling and the verification.
When to use which path
ados-deploy works on a repo checkout, because it builds the Mission Control
image from the repo and pushes Convex functions from it.
Invoke it
From a checkout, through the npm script:-- goes to ados-deploy. The script runs
cargo run --release --quiet --manifest-path tools/deploy/Cargo.toml --.
Directly with cargo:
tools/selfhost/docker-compose.yml, otherwise it clones to
$HOME/ADOSMissionControl. Override with ADOS_DIR and ADOS_REPO_URL. It
then launches, in order of preference, a cached or committed ./ados-deploy
binary, the matching release asset, or a cargo build. Extra arguments pass
through.
With no action the tool opens an interactive menu. On a non-terminal (CI, or
a piped invocation) it asks you to pass an action instead.
Actions
An unrecognised action falls through to the menu.
Dry-run first
--plan (alias --dry-run) prints the files the deploy would write and the
commands it would run, in order, then exits without touching anything.
Secrets are redacted in the plan output. The order is the same one the real
run executes.
Flags
Rendering and interactivity
Deploy answers
Supply these to run the whole flow without prompts.
An unknown flag is ignored rather than rejected, so a newer wrapper script
stays compatible with an older binary.
ados-deploy does not set up TLS or a
tunnel; see
Expose the stack through a Cloudflare tunnel.
What a deploy runs
The plan is ordered and idempotent. For a full self-hosted deploy:- Preflight: Docker running, ports free, Node present.
- Write the generated config files.
- Start the Convex backend and dashboard.
- Wait for Convex to answer on
/version. - Retrieve the admin key (
docker compose exec -T convex-backend ./generate_admin_key.sh). - Push the Convex functions.
- Generate and set the auth keys (
JWT_PRIVATE_KEY,JWKS). - Set the Convex env vars:
SITE_URL, plusMQTT_BROKER_URLandVIDEO_RELAY_URLwhen those services exist. - Set the relay secrets on Convex:
MQTT_AUTH_RELAY_SECRETandVIDEO_RELAY_SECRET, for each service this deploy starts. - Create the broker password file.
- Seed the broker ACL from
tools/mqtt-bridge/deploy/acl.conf, only when absent. - Build and start the remaining services.
- Verify the GCS, the video relay and the MQTT port are reachable.
--convex-managed-url, steps 3 to 9 are skipped. With
--mqtt-managed-url or --no-mqtt, steps 10 and 11 are skipped. On a
plain-HTTP host the plan ends with a note that browser cloud mode needs HTTPS.
Because the plan is idempotent, upgrade reconstructs the config from the
deployed .env and runs it again.
Files it writes
Paths are relative to the repo root; commands run from the root.Services
The tool starts the services intools/selfhost/docker-compose.yml:
convex-backend, convex-dashboard, mission-control, mosquitto,
mqtt-auth-sync and video-relay. What each one does is on
All-in-one stack. It checks
for port conflicts before it starts anything; the default ports are in the
port table.
Check and tear down
teardown runs docker compose down. Purging volumes is a separate opt-in,
so a teardown does not destroy your Convex data.
To roll back a bad upgrade, check out the previous commit of the repo and run
upgrade again. The image is rebuilt and the functions re-pushed from the
checkout, so the checkout is the version you run.
Next steps
All-in-one stack
Every step the tool runs, by hand.
Ports and troubleshooting
The port table and the common failure modes.