Enterprise Readiness
This page is a single-stop reference for a procurement reviewer or a DevOps / security engineer evaluating quant-transfer-guard (QTG) for deployment. Every claim below is grounded in the shipping source tree and CI configuration — file paths are given so you can verify each control yourself. QTG is positioned for small quant teams that need to protect themselves against their own runaway or buggy strategy code and against a malicious or buggy signer — not as a Fireblocks-tier custody product hardened against a database-internal adversary. Read Known Limitations & Threat Boundary before deployment; the accepted boundary is stated explicitly there. For the narrative defense-in-depth walkthrough, see the Security Model. This page is the condensed, verifiable control inventory.1. CI Gates
QTG ships a suite of GitHub Actions workflows under.github/workflows/. Each enforces a single invariant; together they form the merge gate.
Status note for reviewers. The repository’s CI auto-run triggers are currently set toworkflow_dispatch(manual) while the project is pre-release and GitHub Actions minutes are conserved. The gates below are run locally on every change before merge and are restored topush/pull_requesttriggers at release. The workflow definitions are the authoritative description of what each gate enforces.
Two boundary controls outside the CI matrix are worth noting for procurement:
- Open-core split. QTG is distributed open-core. The OSS distribution is AGPL-3.0-or-later; commercial Pro features are governed by a separate EULA. The
pro-boundaryandpro-graceful-importgates mechanically guarantee the OSS artifact never embeds or hard-depends on Pro code. For AGPL-averse buyers, a commercial relicense is available (contact below). - Startup schema validation. Independent of CI, the server runs
boot_validate_schema_invariants()at boot, which verifies schema existence and Alembic-head consistency before serving traffic.
2. Signer Model
The default EVM proving lane uses a local AWS KMS signer running in your own AWS account.- Implementation:
src/qtg/infrastructure/signers/aws_kms_evm.py(AwsKmsEvmSigner). - Key custody: QTG holds only a KMS key ID (
MG_LOCAL_SIGNER_KMS_KEY_ID), never private key material. Signing is an API call to KMS; the key never leaves the KMS HSM boundary. Even a fully compromised QTG host gains only the ability to request signatures — bounded by the approval gate and only while access is maintained. - Key spec validation: at health-check / bootstrap the signer asserts the KMS key is
ECC_SECG_P256K1, usageSIGN_VERIFY, and stateEnabledbefore it is trusted; a misconfigured key fails closed (INVALID_KMS_KEY_METADATA/INVALID_KMS_KEY_STATE). - Payload hash pinning: before signing, the signer recomputes
sha256(payload)and rejects the request unless it matches bothrequest.payload_hashandsigning_intent.allowed_payload_hash(PAYLOAD_HASH_MISMATCH/ALLOWED_PAYLOAD_HASH_MISMATCH) — the signer will not sign a payload that differs from the one the caller declared. - Transient vs fatal: KMS throttling / timeout / key-unavailable map to a temporary error (
SIGNER_UNAVAILABLE, retried); rejections map to fatal (SIGNER_REJECTED). - Configuration:
MG_LOCAL_SIGNER_BACKEND=aws_kms_evm,MG_LOCAL_SIGNER_KMS_KEY_ID,MG_LOCAL_SIGNER_AWS_REGION. AWS credentials use the standard AWS SDK default credential chain; an explicit profile is optional.
Signer alternatives
The signer is an interface, not a hard dependency on KMS. Two other implementations ship:
A development-only plain private-key signer also exists; it holds the key in process memory and must not be used with real funds — migrate to KMS before moving value.
3. Signer Protocol
All signers implement theSignerProtocol runtime-checkable protocol in src/qtg/domain/protocols.py:
SignRequestcarries a typedSigningIntent(action, asset, amount, destination,max_fee_usd,chain_family,allowed_payload_hash) plus the payload and its hash — the signer always sees the declared intent alongside the bytes.health()is invoked at bootstrap so a broken or misconfigured signer is surfaced at startup, not at first fund movement.- The same protocol file defines the
ExecutorProtocol(preflight/prepare/submit/observe/recover/health), so every transport adapter follows one auditable contract.
4. Callback Authentication (HMAC v3)
Outbound callbacks to your receiver are signed with HMAC-SHA256 v3 and defended against replay. Implementation:src/qtg/callback_auth.py.
- Headers:
x-qtg-callback-signature,x-qtg-callback-timestamp,x-qtg-callback-nonce,x-qtg-callback-signature-version(v3). All four are required; any missing → reject. - Canonical string:
timestamp \n nonce \n sha256(body)— the signature covers the timestamp, nonce, and a hash of the full body, so tampering with any part invalidates it. - Constant-time compare: signatures are compared with
hmac.compare_digest(no timing leak). - Timestamp window: requests outside
max_age_seconds(default 300s) are rejected (callback timestamp out of range). - Nonce replay defense: a
nonce_recorderhook records each nonce; a duplicate nonce is rejected (callback replay detected). Receivers must persist seen nonces — see the Callback Verification Contract for the receiver-side requirement. - Delivery durability: callbacks are dispatched from a durable outbox with retry, so a transient receiver failure does not lose the event.
- URL allowlist: callback target hosts are constrained by an operator-configured allowlist (
MG_CALLBACK_ALLOWED_HOSTS_CSV), with an SSRF / DNS-rebind guard on dispatch.
5. Address Allowlist Model
On-chain destination (and, where applicable, source) addresses are checked against a database-backed allowlist at both creation and dispatch time — revoking an address stops the next dispatch even for already-approved, queued movements. Implementation:src/qtg/application/services/address_allowlist.py.
Postures
Everyaction_type a real executor or template can introduce has exactly one posture entry in a single source-of-truth map (ADDRESS_GUARD_POSTURE); a completeness gate fails loudly if a new on-chain adapter is added without one.
chain_match pin
Selected actions additionally require a strict(address, chain_id) match — an allowlist row scoped to the wrong chain does not satisfy the check, and a NULL-chain row is rejected under strict mode. cctp_burn carries chain_match=True. (cctp_mint deliberately does not: the mint recipient is bound by the Circle attestation, so a wrong-domain mint fails attestation rather than diverting funds.) is_address_allowed(..., require_chain_match=True) returns False when chain_id is absent — fail-closed by construction.
Enforcement properties
- Enforced at the database level inside the executor at dispatch — not a client-side check that can be bypassed.
- Address normalization is chain-family-aware (EVM addresses lower-cased for storage and comparison).
- A non-allowlisted address raises
FatalMovementErrorwitherror_code=ADDRESS_NOT_ALLOWED; a misconfigured strict check raisesMovementValidationErrorwithADDRESS_VALIDATION_MISCONFIG. The movement does not proceed.
6. Known Limitations & Threat Boundary
QTG states its boundaries explicitly. Reviewers should weigh these against their threat model.”DB-write = game over” — the accepted boundary
Reference:docs/reference/security/db-access-control-boundary.md.
QTG’s submit-time guards (signed-recipient invariant, signed-amount/value envelope) close the malicious / swapped / buggy signer vector and a tamper of the prepared action / calldata alone. They do not close a full-DB-row tamper that rewrites the trusted anchor (ctx.intent, ctx.node_config, or the allowlist itself) in lockstep with the signer output — the equality / cap checks would then compare tampered-against-tampered.
This is a deliberate, documented boundary, not an oversight. An in-band DB integrity hash raises no real bar against an adversary who can already write the movements DB (they can forge the hash too) — it would be security theater. Under QTG’s positioned threat model, DB-write access is treated as out of scope: an attacker with write access to the movements DB has already won.
The proportionate mitigations are deployment / infrastructure controls the operator owns, in descending value:
- Least-privilege DB roles (runtime app role holds only the DML it needs; schema / allowlist / approval tables owned by separate roles).
- Separate approval authority (allowlist / approval changes require a credential path distinct from the runtime app credential).
- Immutable / append-only audit shipped to an external sink the app role cannot rewrite.
- Network isolation + credential hygiene + TLS in transit.
- Backups + point-in-time recovery as the recovery floor.
node_config-only tamper can inflate the OFT (Stargate / USDT0) and ERC20 amount ceilings — a bounded over-transfer to the still-pinned recipient — which is the same accepted DB-write boundary.
CCIP lane: signed-artifact invariant
Reference: CCIP Lane. The CCIP bridge lane is implemented and participates in the submit-time signed-artifact invariant. Before the out-of-process sidecar broadcasts, QTG decodes the signed legacy transaction and pins router, destination chain selector, receiver, token, amount, native fee, signer identity, and source chain id against the trusted intent. A mismatch fails closed asccip_signed_artifact_mismatch. The most operationally-proven lane today remains the Upbit ↔ Bithumb CEX withdrawal path, while the EVM lanes carry submit-time signed-artifact guards.
Other operating notes
- PostgreSQL only (v0.1.0+). There is no SQLite runtime path; tests run against real PostgreSQL.
- Approval gate is mandatory. Every movement stops at
PENDING_APPROVAL; agents can propose but not approve. Auto-approve policies evaluate automatically but do not remove the stage. - Outflow velocity cap covers the CEX-balance lanes; on-chain bridge lanes are out of scope for the cap by construction.
7. Reporting Security Issues & Support
- Vulnerability reports: email security@jerryquanthouse.com — do not open a public GitHub issue for security vulnerabilities. Acknowledgement target 48 hours; fix / mitigation plan within 7 days for critical issues. See
SECURITY.mdfor the full policy and coordinated-disclosure terms. - Commercial / procurement enquiries (including AGPL relicensing for the OSS distribution and Pro EULA terms): contact JephaLabs through your existing commercial channel or the GitHub organization contact path.
Reviewer tip. Every control on this page is verifiable in the source tree at the path cited. If a claim here ever diverges from the code, the code is authoritative — please report the discrepancy via the security contact above.