Skip to main content

01 - Local Setup

Start the QTG server locally and confirm the health check.
QTG CLI and MCP quickstart

QTG local setup in the CLI.


Prerequisites: a container engine

qtg init brings up PostgreSQL via Docker Compose, so you need a running container engine first. Install one of: Confirm it works before running qtg init:
If docker is missing or docker info fails, install/launch the engine above; qtg init will still write your .env but will skip bring-up. qtg init is the single front door. It interactively writes a signing-capable dev .env and optionally brings up PostgreSQL + runs migrations:
The Postgres data volume is declared external so a stray docker compose down -v cannot delete it — which also means Compose will not create it for you. Create it once on a first install. If Compose reports it missing on a machine that has run QTG before, do not create it: the database volume is gone, and recovering it comes first. qtg init prompts for:
  • Network mode (mainnet / testnet) — and overlays the matching Circle Gateway / CCTP Iris endpoints automatically.
  • Signer private-key storage1Password (recommended) is the default. Select an existing secret, store an operator-generated development key in a new 1Password item, or enter an op:// reference. QTG never generates the key; it validates the resolved signer against the confirmed public EVM address and stores only the reference and non-secret OP_ACCOUNT account pin in .env. Plaintext .env (development only) remains an explicit, warned alternative for a low-value key; it writes that key unencrypted to .env.
  • Exchange API keys — a multi-select for the Free venues (Upbit / Binance / OKX / Bybit / Coinbase); skip any you don’t need yet.
DB URL, callback HMAC secret, allowed hosts, and RPC defaults are auto-filled. op mode removes the EVM signer private key from the .env; it does not move callback, exchange, database, RPC, or other application secrets. That broader configuration/secret boundary is out of scope here. Useful flags: --network mainnet|testnet, --no-bringup (write .env only), --force (overwrite an existing .env, backed up first), --yes (non-interactive, requires --network + a plaintext key when the signer source is omitted), --repo-root <path>. Non-interactive 1Password setup is local-reference-only: it does not create, edit, or delete an item. Supply all of its public inputs:
--op-account may instead be supplied by ambient OP_ACCOUNT when it agrees with an explicit value. The expected address is required so QTG can fail closed if the reference resolves to another signer. In --yes mode, omitting --signer-source preserves the compatible plaintext flow.
For plaintext --yes mode, prefer supplying the key via the QTG_DEV_SIGNER_KEY environment variable rather than --signer-key — a CLI argument lands in shell history and process listings (ps). The interactive path always uses a hidden prompt and never touches argv.
Then start the dev server. For the recommended 1Password source, the local 1Password app/session and configured op executable must remain available at bootstrap and whenever QTG signs. Use the source-aware command; it clears ambient account and unsupported unattended selectors so the OP_ACCOUNT pin in .env is authoritative:
If you explicitly selected the plaintext development source, use the ordinary --env-file command instead:
docker compose up <app> is not the dev signing path — the containerized app services intentionally blank the signer config. Use the matching local uvicorn command above for a signing-capable dev server; this onboarding does not configure Compose signing or unattended 1Password access.
Treat an edit to the selected 1Password item as signer rotation, not a transparent configuration change: verify the signer address before use. If qtg init --force replaces an existing .env, its mode-0600 .env.bak.* backup can still contain a plaintext key or other sensitive configuration and is not deleted automatically.

Check the CLI surface

The current QTG control plane has three entry points: After you seed an operator or agent HMAC key, point the CLI at the local API:
qtg-agent and qtg-mcp are read + propose surfaces. They can inspect balances, movements, timelines, authority state, and template proposals, but they do not run an approved movement by themselves.
To tear down docker: ./scripts/quickstart.sh --down

Troubleshooting bring-up

qtg init writes your .env first, then attempts bring-up — so a bring-up failure never loses your config. If it prints Bring-up skipped (…), your .env is already in place; fix the engine and run the migrations manually (below). Common causes: Re-run migrations manually after fixing the engine (global bucket + your network only — see Step 4):

Manual path

If you prefer to configure each step yourself:

Step 1: Clone and install

Step 2: Configure environment

Open .env and set at least these three values:
MG_NETWORK_MODE has no default — the server refuses to boot if it is unset. Set it to mainnet or testnet. The shipped .env.example uses mainnet.

Key Environment Variables Summary

RPC endpoints — pre-populated

QTG ships a curated PublicNode-based default RPC table covering ~24 EVM chains. You do not need to set MG_EVM_RPC_ENDPOINTS_JSON for the quickstart to talk to lanes like Base Sepolia, Arbitrum One, Ethereum mainnet, etc. When you go to production, override per-chain with a paid provider — see 04-going-live.md and rpc-defaults.md.
For the full environment variable list, see the Bootstrap reference.

Binance Master / Sub Logical Venue Alias Example

To use a Binance main account and an experimental sub-account at the same time, set .env like this. The sub binding must use a credential-less shape, and balance reads use the master key to call /sapi/v3/sub-account/assets. Because of that, the master API key needs the Enable Sub-Account Management permission. Binance sub-account asset/transfer APIs take the sub-account email in the email / fromEmail parameter, not a numeric subUserId. Because of that, the current sub_account_uid field name stays the same, but the value must be an email.
After that, you can use an alias such as source_exchange: "binance_lab" directly in template/runtime bindings. By contrast, the external-withdrawal proving adapter cannot be created directly from a sub binding and must use the master binding or the capital transfer subsystem.

Step 3: Start PostgreSQL

The manual path does not run qtg init, so nothing has started a database yet. Bring up the same Compose service the quick path uses — no external PostgreSQL is required, and the credentials match the MG_DATABASE_URL default above:
If Compose reports that the external volume is missing on a machine that has been running QTG before, do not create it and start over — the database volume is gone and recovering it comes first. If you would rather point at a PostgreSQL you already run, set MG_DATABASE_URL to it instead and skip this step; the migrations below then apply to that database.

Step 4: Start the Server

Database schema is managed by Alembic migrations. Run uv run qtg init (which runs migrations automatically) or apply manually. Migrate the global bucket plus the one network bucket matching MG_NETWORK_MODE — a single database holds exactly one network bucket (the schema guard rejects the second):
If your .env uses the recommended 1Password signer source, use the source-aware command from the quick path so the file’s OP_ACCOUNT pin is not overridden. If you explicitly configured a local plaintext development signer, pass --env-file .env so the non-MG_-prefixed key variable lands in the process env:

Step 5: Health Check

Expected response:

Understand the Run Modes

QTG runs in two modes: The quickstart starts in API-only mode. Real exchange/chain integrations are covered in 04-going-live.md.

Dashboard (Optional)

The dashboard is available at http://localhost:5173 (dev) or http://localhost:3000 (Docker).
See operator-network-access.md for production exposure topology.

Verification Commands


Next: 02-first-movement.md - register your first template and create a movement