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:2. Configure Exchange Credentials
To use the CEX executor, you need exchange API keys: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:qtg.main rejects local KMS
settings. Explicit one-off preflight tools retain their separately reviewed MG_LOCAL_SIGNER_*
inputs; check those inputs with:
Details: Signer protocol, CCTP bridge lane
4. Enable HMAC Auth
4-1. Create API Keys
key_id and secret safely.
4-2. HMAC Signing on the Client
Canonical string layout: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.
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
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:- Signature verification -
X-QTG-Callback-Signatureheader - Timestamp freshness - within 5 minutes
- Reject duplicate nonces - replay defense
- 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
Allowlist writes are configuration changes with maker/checker. Two supported paths: Dashboard (normal path). WithMG_CONFIG_REMOTE_MUTATION_MODE=proposal_only, an admin or
operator adds the address under Governance -> Destination addresses (/governance/allowlist).
That creates an allowlist.add proposal; a different admin applies it under
Governance -> Configuration proposals. The direct POST /dashboard/allowed-addresses route
returns 409 CONFIG_PROPOSAL_REQUIRED.
Host-maintenance CLI (audited maker/checker bypass).
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-idmatches every chain. - A CEX withdrawal also needs the destination registered in the exchange’s own
whitelist; QTG checks both and fails with
ADDRESS_NOT_WHITELISTEDwhen the exchange address book does not match.
Details: Runbook — Address Allowlist Management · CEX Stuck Node RecoveryAn allowlist row makes a destination eligible; it does not approve a movement. Templates are registered the same way (a
route_template.register proposal) — see
Registering Templates Outside Dev.
6-1. Onboard an Agent (MCP / qtg-agent)
An agent can read and propose, never approve or execute. It cannot register templates or
allowlist entries, and every movement it creates stays in PENDING_APPROVAL even when
auto-approve is enabled (agent_auto_approve_skipped). So an agent cannot set itself up; an
operator prepares its fence first:
- Agent key.
seed_auth_client --role agent(above). Note the returnedclient.id; the authority binds to it. - Allowlist the agent’s destinations (section 6). An agent proposal may only target an
EVM address that is allowlisted and bound to its authority, with a matching
chain_id; anything else is rejected asdestination_outside_authority_scope. - Agent authority. An admin creates an
agent_authority.createconfiguration proposal and a different admin applies it (Governance -> Agent authorities). It binds the agent’sclient_idto: allowed protocol families, signer profiles (signers), the allowlisted address IDs (allowed_address_ids), a discovery budget, and a promotion policy. - Give the agent its connection.
QTG_API_URL,QTG_KEY_ID,QTG_HMAC_SECRET_FILE, andQTG_AGENT_NAMESPACE=agent/<authority-key>forqtg-agent/qtg-mcp.
The agent must use values its authority allows (protocol_family,signer_profile, destination). Point it atqtg_authority_describefirst instead of letting it guess. CEX routes are not agent-proposable.
7. Docker Deployment
qtg-testnet and two throwaway test
databases; qtg-testnet points at its own qtg_v3_testnet database (JEP-616), so
if that database does not exist or has not received the testnet migration bucket,
qtg-testnet fails its boot schema check.
Service layout:
One database holds one network bucket, so run one of the two app services against a shared database —
qtg-testnet gets its own database in the compose file.
See operator-network-access.md for production exposure topology.
Docker uses the project-root.envthroughenv_file: ../.env.MG_DATABASE_URLis overridden in docker-compose to use the PostgreSQL hostname.
8. Live Cutover Checklist
Things to confirm before production cutover:-
MG_WORKERS_ENABLED=trueconfigured - 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=trueplus API keys issued - Callback secret configured plus receiving server ready
-
MG_CONFIG_REMOTE_MUTATION_MODE=proposal_onlyand at least two admin identities (maker ≠ checker) - Address allowlist registered (on-chain paths and CEX withdrawal destinations)
- Route templates registered via applied configuration proposals
- 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:- Pick a provider per chain (Alchemy, Infura, QuickNode, or self-hosted).
-
Override per-chain in
.env— keep defaults for any chain you do not yet intend to enable: -
In air-gapped or regulated environments, also set
MG_EVM_RPC_USE_DEFAULTS=falseto block any silent fallback to PublicNode.
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.