Getting Started
A single-page consolidated reference for getting QTG running, authenticated, and verified. If you want a guided multi-step walkthrough instead, usedocs/quickstart/ (4 documents, ~30 minutes end-to-end).
Supported platforms
Toolchain:
- Python 3.14+
- uv package manager
- PostgreSQL (Docker Compose provided)
- Exchange API keys (live mode only)
- AWS KMS key (EVM proving lanes) or 1Password CLI (dev quickstart)
Quickstart
MG_NETWORK_MODE. Follow the ordered API-only local setup; running uvicorn before those prerequisites leaves Core without its required schema.
The local configuration must supply:
MG_DATABASE_URLmatching the PostgreSQL password and host connectionMG_CALLBACK_HMAC_SECRETreplacing the template valueMG_CALLBACK_ALLOWED_HOSTS_CSVMG_NETWORK_MODEset tomainnetortestnet
Docker
qtg-mainnet uses the qtg_v3 database from the Compose defaults. Do not add qtg-testnet to that command: its configured qtg_v3_testnet database needs its own PostgreSQL setup and the testnet migration bucket.
The Postgres data volume is external so that docker compose down -v cannot delete
it. Compose therefore refuses to start when it is absent, which is the intended signal
— see docs/quickstart/01-local-setup.md.
Dashboard
Execution modes
Signer backends
The proving-lane signer is AWS KMS behind the private signer service. For explicit development or evaluation tools without AWS, a local private-key signer provides two key sources:- 1Password CLI (recommended for local) — keys fetched on demand from your vault.
- Plain env var (
plain) — development only; plaintext key held in process memory. Do not use for fund movement volume. No sign-without-expose guarantee.
docs/reference/signers/local-private-key-signer.md and docs/reference/signers/signer-protocol.md.
Multi-signer bootstrap
qtg.main does not accept in-process local or multi-signer bootstrap. Run a private signer service for the selected KMS key and configure that one remote signer in each Core deployment. MG_EXTRA_SIGNERS_JSON remains available only to reviewed explicit tools; Core rejects it.
CCTP local signer readiness
.env (copied from .env.example).
The retained one-shot command is TESTNET-only and can sign and broadcast through
AWS KMS. Its presence is not live approval: use it only with a fresh reviewed
operator approval for the exact run. Pytest has no CCTP broadcast authority.
Binance and OKX master / sub-account models
Binance sub-account bindings are signed through the master credentials — QTG never holds sub-account trading keys. Declare sub bindings withaccount_role: "sub" and sub_account_uid, omitting access_key and secret_key.
For Binance, sub_account_uid is intentionally the sub-account email (Binance sub-account asset and transfer APIs use email / fromEmail); do not put the numeric subUserId / UID there.
Master bindings use account_role: "master" and carry credentials. The Free adapter reads ordinary account balances; QTG Pro registers privileged master-signed sub-account balance reads through /sapi/v3/sub-account/assets, which require the Binance master API key’s Enable Sub-Account Management permission. See .env.example for the full binding JSON shape.
OKX uses the same credential boundary: the master binding carries access key,
secret, and passphrase; the sub binding carries only the OKX sub-account name in
sub_account_uid. OKX capital transfers support only explicit FUNDING and
TRADING account types and only master↔sub topologies. A submit response is not
completion: QTG keeps the request pending and reconciles the exact request tuple
through OKX transfer state before finalizing reservations. QTG Pro is required. New capital-transfer mutation is default-off: explicitly set
MG_CAPITAL_TRANSFER_MUTATIONS_ENABLED=true only after installation and operator review. This does not enable the independent follow-up sidecar. Set
MG_CAPITAL_TRANSFER_FOLLOWUP_ENABLED=true to run that automatic reconciliation
sidecar; otherwise the durable row and source hold remain for manual
investigation, with no automatic resubmit. QTG prepares and durably stores the
non-secret OKX clientId before HTTP; submit and follow-up reuse that stored
identity rather than deriving a second copy after the crash boundary.
CEX logical venue alias
MG_CEX_BINDINGS_JSON maps an alias such as binance_lab to the existing Binance provider. If an alias exists, runtime proving and balance both resolve binding-first. Legacy MG_BINANCE_* settings remain as fallback.
Auth model
Settings.auth_enabled defaults to true, so an environment that omits MG_AUTH_ENABLED requires HMAC signatures for its /v3/** requests except /healthz. The shipped .env.example intentionally sets MG_AUTH_ENABLED=false for the local API-only quickstart; do not carry that value into an operational deployment.
Inbound request HMAC
Required headers:X-QTG-Key-IdX-QTG-TimestampX-QTG-NonceX-QTG-Signature
PATHis the routed, percent-decoded path the server dispatches on — no origin, no query string.QUERYis the query exactly as sent: no leading?, no sorting, empty string if absent. Sorting before signing transmits bytes you did not sign, and every such request 401s.
examples/ directory.
RBAC — 3 roles
Each API key is bound to exactly one role, enforced per route by the HMAC auth middleware:
All mutating routes are mirrored into an append-only
audit_events table. Query via GET /v3/audit/events (canonical, admin + operator) or the legacy GET /v3/registry/audit. For agent-namespace tenant isolation, see docs/reference/api/v3-endpoints.md.
Outbound callback HMAC (v3)
Headers:X-QTG-Callback-TimestampX-QTG-Callback-NonceX-QTG-Callback-Signature-Version: v3X-QTG-Callback-Signature
docs/callback-verification-contract.md. A working receiver example is provided in the repository’s examples/ directory.
Schema bootstrap
PostgreSQL only since v0.1.0. Auto-creating tables at startup is no longer supported. A fresh DB requires the global Alembic bucket plus exactly one network bucket matchingMG_NETWORK_MODE:
- Heads are per-bucket — there is no single global “head” revision; upgrade the global bucket and only the selected network bucket to their own
@head. - Startup validation: on boot the server checks that the schema exists and the alembic head matches before accepting traffic — a drifted or unmigrated DB refuses to boot.
- Existing databases (schema drift, live deployment): managed by the same
alembic_v3/migration chain. - One-off data backfills:
scripts/or explicit operational runbooks. - Local and container dependency installation: all aligned on
uv. - Minimum Python:
3.14(repo-local default + Docker baseline both aligned).
MG_NETWORK_MODE is required and has no default (mainnet | testnet). The server refuses to boot if it is unset; .env.example ships MG_NETWORK_MODE=mainnet.
Runtime surfaces
QTG runs as a single FastAPI service with a few operator-facing surfaces:- REST API — Core mounts its
/v3/*movement, template, registry, and signer endpoints, plus/healthz. Optional Pro routes mount only when the commercial package is installed and configured. - Dashboard — a React SPA served against the
/dashboard/*endpoints (Gateway balances, CCTP transfer tracking, movement management, route lookup). Optional Pro dashboard surfaces likewise depend on the installed Pro package. - Runtime workers — when
MG_WORKERS_ENABLED=true, 8 always-on workers (dispatcher, observer, recovery, callbacks, expiration, proposal expiry, executor health, audit promotion) run and execute real integrations, plus conditional sidecars behind their own flags. - CLI tools — preflight, seed, and import utilities for setup and operations.
examples/ (a callback receiver and a signed sender) and docs/ (design, reference, and operational docs).
Verification commands
GET /healthz returns:
Important runtime nuances
- Auto-approve. When
MG_AUTO_APPROVE_ENABLED=trueandstrategy_idis provided, policy-matched requests becomeAPPROVEDimmediately and manual approval is skipped. - Address allowlist. On-chain executors apply the DB-level address guard at dispatch time, not at template registration.
- Callback v3. Nonce-based replay protection is implemented; receivers must store and reject seen nonces.
- Signer health. Bootstrap authenticates private signer health and pins the expected EVM address.
- EVM proving signer.
qtg.mainusesRemoteSignerProxy; the private signer service owns AWS KMS. Local signer variables are reserved for explicit one-off tools and are rejected by Core. - EVM RPC endpoints. Default to PublicNode URLs for ~24 mainnet + testnet chains so the dev quickstart works out of the box. Per-chain overrides in
MG_EVM_RPC_ENDPOINTS_JSONalways win; setMG_EVM_RPC_USE_DEFAULTS=falseto disable the table entirely. Live broadcast tools (*_live_preflight, Stage 3 harness) ignore defaults regardless — seereference/rpc-defaults.md.
Where to go next
- First movement, dry-run:
docs/quickstart/02-first-movement.md - Template patterns by lane:
docs/quickstart/03-template-cookbook.md - Going live (auth, callbacks, workers, Docker, allowlist):
docs/quickstart/04-going-live.md - API reference:
docs/reference/api/v3-endpoints.md - Runbooks:
docs/runbooks/