Data Model Reference
Source files:Defines the full data model of the qtg v3 runtime. The current schema has roughly 54 tables across two logical schemas; the ERD below shows a simplified view of the core movement + registry structure documented in detail in this page.src/qtg/infrastructure/db/models.py,session.py
- Plan Template layer — design-time definition of the movement plan
- Request layer — runtime instance
- Audit layer — events / artifacts / callback records
- Registry layer — persistence for executor/signer registrations
Schema layout (PostgreSQL, PG-only since v0.1.0)
QTG runs on PostgreSQL only and splits its tables into two logical schemas defined inmodels.py via __table_args__:
public— deployment-global, network-independent identity tables. Exactly three:api_clients,api_client_keys,nonce_registry- Alembic head is tracked in
public.alembic_version_global.
qtg_network— the logical schema used by every other model (~51 tables). At runtime SQLAlchemy rewritesqtg_networkto the concrete network bucket viaschema_translate_map={"qtg_network": <network>}, and each session runsSET LOCAL search_pathfor its network class.
mainnetandtestnet— one full copy of theqtg_networktable set per network class. Each carries its ownalembic_versionhead.
qtg init --network <network> creates public plus exactly the selected mainnet or testnet bucket. At startup, boot_validate_schema_invariants() checks those two schemas and their Alembic heads. The unselected bucket is not required or queried, even though the runtime engine registry can represent both NetworkClass values. See Bootstrap & Configuration for the bootstrap chain.
Table families
Beyond the core movement/registry tables detailed below, the schema includes these families (all inqtg_network unless noted):
- Identity (public):
api_clients,api_client_keys,nonce_registry - Operator / policy:
allowed_addresses,auto_approve_policies,approval_policy_targets,budget_ledger,execution_stats,template_proposals - Audit:
audit_events,audit_outbox - Agent wallet top-up:
agent_authorities(+agent_authority_signers,agent_authority_allowed_addresses,agent_authority_budget_ledger),agent_wallet_funding_envelopes,agent_wallet_topup_ledger - Capital transfers:
capital_transfer_requests - Balance reservations:
balance_reservations,balance_reservation_pools,balance_snapshots - Hyperliquid (6):
hyperliquid_master_accounts,hyperliquid_agent_wallets,hyperliquid_strategy_bindings,hyperliquid_nonce_leases,hyperliquid_emergency_states, plus related state - CCIP (6):
ccip_router_registry,ccip_lane_registry,ccip_chain_token_lane,ccip_chain_source_token,ccip_router_preapproval,ccip_send_intent - Stargate (Pro):
stargate_pools(+stargate_pool_audit),stargate_chain_registry(+stargate_chain_registry_audit),stargate_path_baseline(+stargate_path_baseline_audit),stargate_send_intent - Bridge send intents:
usdt0_send_intent,stargate_send_intent,ccip_send_intent - Runtime / signer ops:
evm_nonce_reservation,worker_heartbeats,signer_rotation_events
Entity Relationship Diagram
1. MovementPlanTemplate
Table name:movement_plan_templates
Top-level entity of the movement plan. A single template_key may have multiple versions.
PlanTemplateStatus enum values:
draft, active, archived
Check constraint: a CHECK enforces NOT auto_approve_enabled OR (daily_cap_amount IS NOT NULL AND daily_cap_asset IS NOT NULL) — auto-approve-enabled templates must define a daily cap.
Internal UUID policy
Internal persisted UUID PKs are created via the common helpernew_internal_uuid(),
and the current implementation uses stdlib uuid.uuid7().
- DB/API types remain plain
UUID - Purpose: improved insert locality and near-time-ordered UUID generation
- Current query ordering semantics still use
created_at - Using
id(uuid7)ordering is a follow-up optimization candidate
2. MovementPlanVersion
Table name:movement_plan_versions
Version management unit for a template. Stores node/edge construction, approval/completion policy, and a risk-controls snapshot.
Unique constraint:
(template_id, version) — the same version number cannot be reused within a template.
Thetransport_family/source_venue/destination_venue/assetcolumns are denormalized routing metadata stored at version-compile time for fast filtering and reporting; the authoritative source remains the node graph.
GraphShape enum values
completion_policy JSON structure
CompletionAssurance values:
provider_completed— provider (exchange/protocol) reports completiondestination_observed— on-chain receive at destination detecteddestination_credited— destination deposit confirmeddestination_finalized— destination chain finality confirmedprotocol_finalized— protocol-level finality confirmed
approval_policy JSON structure
Stored and covered by the plan-graph hash, but not read by the runtime. Every movement entersPENDING_APPROVAL regardless of its contents.
risk_controls JSON structure
Free-form and carried whole into the compiled-plan hash. Onlymax_amount is enforced, as a
per-node amount cap at dispatch; min_amount is read for route filtering. Any other key is inert.
3. MovementPlanNode
Table name:movement_plan_nodes
Definition of a DAG graph node. Belongs to one plan version and is instantiated as MovementRequestNode at execution time.
Unique constraint:
(plan_version_id, node_key) — node_key cannot be duplicated within a version.
node_kind values
Defined by theNodeKind enum:
action_type examples
executor_selector JSON structure
The matching criteria used to choose an executor. Theresolve_bindings() function binds the actual executor based on this value.
signer_selector JSON structure
Criteria for choosing the signer on nodes that require signing.config (node_config) JSON structure
When the node executes, this is passed to the executor in thenode_config field of ExecutionContext. The contents differ by node kind.
CEX withdrawal node config example:
timeout_policy JSON structure
retry_policy JSON structure
4. MovementPlanEdge
Table name:movement_plan_edges
DAG graph edge definition. Manages transition conditions and priority between nodes.
EdgeType enum values
5. MovementRequest
Table name:movement_requests
Runtime instance of a movement execution request. References template/version and tracks the full lifecycle.
auto_approve_result (policy trace)
auto_approve_result is always populated as the auto-approve policy trace — a machine-readable record of why the request was or was not auto-approved. Non-evaluated paths still seed a trace with a machine_reason (for example manual_required); the actual approval outcome is unchanged by the trace. This makes the auto-approve decision auditable even when the policy engine did not act.
RequestState state transitions
Terminal states:COMPLETED, FAILED, REJECTED, EXPIRED, CANCELLED
intent JSON structure
Carries the movement intent passed by the caller. Structure varies by template.input_params JSON structure
Concrete parameters required for execution.callback_config JSON structure
- If
urlis present, the runtime requiresMG_CALLBACK_HMAC_SECRETand a callback host allowlist. - If the allowlist or secret is missing, request creation fails closed.
current_frontier meaning
Array ofnode_keys for nodes currently executable or executing. The frontier is updated as the DAG progresses.
- Initial creation: node_key of the root node(s)
- After a node completes: advance to the successor node’s node_key
- Fully complete: empty array
[]
ApprovalStatus / ReservationStatus enum values
ApprovalStatus:pending, approved, rejected
ReservationStatus: none, held, consumed, released
none— before approvalheld— resource held after approvalconsumed— used after normal completionreleased— released after failure/cancel
6. MovementRequestNode
Table name:movement_request_nodes
Runtime instance of MovementPlanNode. Tracks individual node execution state, provider references, and binding info.
Unique constraint:
(request_id, node_key) — node_key cannot be duplicated within a request.
NodeState state transitions
Terminal states:COMPLETED, FAILED, CANCELLED, SKIPPED
executor_binding JSON structure
signer_binding JSON structure
provider_refs JSON structure
Accumulates provider-side reference info returned by the executor. Keys vary by provider/protocol. CEX withdrawal example:provider_ref_id is the representative ID chosen by the select_primary_provider_ref() function from provider_refs by priority.
7. MovementArtifact
Table name:movement_artifacts
Stores evidence (prepared action, proof, signed payload, etc.) produced during execution.
Note: in the Python model this is mapped asartifact_metadata, but the DB column name ismetadata.
artifact_type values
content_format values
Typically one ofjson, hex, or base64.
Storage strategy
content_inline: small data is stored inline directlycontent_uri: large data references an external store URI (S3, etc.)- Both fields are NULLABLE, but at least one must be present
8. MovementEvent
Table name:movement_events
Audit-trail table that records every state transition and major event in chronological order.
event_type values
actor_type / actor_id combinations
9. MovementCallbackOutbox
Table name:movement_callback_outbox
Outbox-pattern table that manages callbacks to be delivered to external systems.
CallbackOutboxStatus enum values
Retry strategy
- On failure,
next_attempt_atis set to2 ** min(attempts, 6)seconds of backoff - When
max_attempts(default 5) is reached, transition todlqstate - The callback_dispatcher worker periodically polls records in
pendingstate
MovementCallbackOutbox.idalso follows the internal UUID policy and is thus uuid7-based.enqueue_callback()creates the persisted outbox rowid, and mirrors that value ascallback_idin the payload.
10. ExecutorRegistryEntry
Table name:executor_registry
Persists executor registration info. The executor_health worker refreshes it periodically.
11. SignerRegistryEntry
Table name:signer_registry
Persists signer registration info.
12. BalanceSnapshot
Table name:balance_snapshots
Append-only store of the latest per-venue balance snapshots. Records Gateway unified USDC balance, CEX, and on-chain balances through the same schema.
Indexes
ix_balance_snapshot_venue_asset_account_fetched—(venue_key, asset, account_type, fetched_at)- index on
fetched_at— for retention cleanup
Lookup semantics
- The latest lookup unit is
(venue_key, asset, account_type). - Latest semantics including tie-breaks:
ORDER BY fetched_at DESC, id DESC. - Both
fresh=trueAPI calls and worker fetches write into the same append-only table.
DB Session settings
Sessions are network-class aware. A single base engine owns the connection pool; per-network engines reuse that pool throughexecution_options(schema_translate_map={"qtg_network": <network>}), so session_for(network_class)() returns a sessionmaker that auto-tags info["network_class"].
pool_pre_ping=True— liveness check when taking a connection from the poolexpire_on_commit=False— keep loaded attribute values after commitsession_for(network_class)— returns the sessionmaker for one network class; anafter_beginlistener issuesSET LOCAL search_pathso unqualifiedqtg_networktables resolve into the right bucketiterate_per_network(...)— runs a coroutine once per network class (used for registry-sync and heartbeat seeding at startup)public-schema models (api_clients,api_client_keys,nonce_registry) use a separate network-independent auth sessionmaker bound to the base engine
MG_DATABASE_URL environment variable (default: postgresql+asyncpg://qtg:qtg@localhost:5432/qtg_v3). PostgreSQL is required since v0.1.0; the process network class is selected by the required MG_NETWORK_MODE setting.