Skip to main content

04 - Going Live

Settings required to move from dry-run to real operation. Enable auth, wire callbacks, start workers, deploy with Docker, and configure the address allowlist.

1. Enable Workers

The key setting for moving from dry-run to live:
Enabled workers:

2. Configure Exchange Credentials

To use the CEX executor, you need exchange API keys:
The distribution ships Upbit, Binance, Bybit, Coinbase, and OKX adapters.
These credentials are plaintext on disk. That is a deliberate tradeoff, and it is survivable — but only if your exchange-side IP restriction and withdrawal address whitelist are configured. See Security Tradeoffs § Exchange credentials before you deposit real capital.

3. Configure EVM / Signer (CCTP, Gateway)

To use on-chain executors:
After configuring the signer, check it with preflight:
Details: Signer protocol, CCTP bridge lane

4. Enable HMAC Auth

4-1. Create API Keys

Generate API keys with the seed CLI:
Store the output key_id and secret safely.

4-2. HMAC Signing on the Client

Canonical string layout:
  • PATH is the routed, percent-decoded path the server dispatches on — no origin, no query string.
  • QUERY is 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.
Required headers:

4-3. curl Example (With Signature)

4-4. Access Scope by Role

Access is governed by a 3-role axis (admin / operator / agent), set per key with --role on seed_auth_client. Each route maps to the set of roles allowed to call it; admin has the widest reach. Capital-transfer surface (operator + admin):
Details: Auth rollout, v3 API endpoints

5. Wire Callbacks

5-1. Callback Configuration

If you set callback: {"url": "..."} when creating a movement, a callback is delivered on every state change.

5-2. Implement the Callback Receiver

The receiving server should validate:
  1. Signature verification - X-QTG-Callback-Signature header
  2. Timestamp freshness - within 5 minutes
  3. Reject duplicate nonces - replay defense
  4. 200 OK response - 4xx is permanent failure, 5xx is retried
Full contract: Callback verification contract Example: Callback receiver example

6. Address Allowlist

The address guard is applied at dispatch time to on-chain executors (CCTP, Gateway, CCIP, Stargate, USDT0, ERC-20 transfer) and to CEX withdrawals. Register the wallet addresses funds move between — not token contracts.

Register Addresses

The dashboard exposes the same mutations behind the writer HMAC boundary (POST /dashboard/allowed-addresses, operator UI /governance/allowlist). Avoid raw SQL INSERTs — they bypass the audit log, actor attribution, and the cooling-period stamp.
  • EVM addresses are normalized to lowercase automatically
  • Revoke with python -m qtg.interfaces.tools.revoke_allowed_address --allowed-address-id <id> --reason ... (soft-delete, preferred over deletion)
  • A row is (chain_family, address, chain_id?)no token scope. An allowlisted address accepts any token, and omitting --chain-id matches every chain.
  • A CEX withdrawal also needs the destination registered in the exchange’s own whitelist; QTG checks both and fails with ADDRESS_NOT_WHITELISTED when the exchange address book does not match.
Details: Runbook — Address Allowlist Management · CEX Stuck Node Recovery

7. Docker Deployment

Name the services. The file also defines qtg-testnet and two throwaway test databases; starting everything runs a mainnet and a testnet app against the same database, and the one whose bucket was not migrated fails its boot schema check. Service layout: One database holds one network bucket, so run one of the two app services — not both.
See operator-network-access.md for production exposure topology.
Docker uses the project-root .env through env_file: ../.env. MG_DATABASE_URL is overridden in docker-compose to use the PostgreSQL hostname.

8. Live Cutover Checklist

Things to confirm before production cutover:
  • MG_WORKERS_ENABLED=true configured
  • Exchange API keys configured and balances checked
  • EVM RPC endpoints configured (if using on-chain paths)
  • Signer configured and preflight passing (if using on-chain paths)
  • MG_AUTH_ENABLED=true plus API keys issued
  • Callback secret configured plus receiving server ready
  • Address allowlist registered (on-chain paths and CEX withdrawal destinations)
  • CEX withdrawal destinations also registered in the exchange’s own whitelist
  • Small-value live test - verify end-to-end with a real small transfer, not dry-run
  • Dashboard access confirmed
  • Callback receiver verification confirmed (signature, nonce, timestamp)
Operational procedures: Secret Rotation · CEX Stuck Node

Replace default RPC endpoints

The quickstart ships PublicNode-based defaults, which are rate-limited and not suitable for live transfers. For production:
  1. Pick a provider per chain (Alchemy, Infura, QuickNode, or self-hosted).
  2. Override per-chain in .env — keep defaults for any chain you do not yet intend to enable:
  3. In air-gapped or regulated environments, also set MG_EVM_RPC_USE_DEFAULTS=false to block any silent fallback to PublicNode.
Live preflight / drill / e2e tools already hard-code defaults off — they require explicit RPC URLs even with MG_EVM_RPC_USE_DEFAULTS=true. Set those explicit URLs before running them. See rpc-defaults.md for the full shipped chain list and the CCIP path caveat.