closegate is alpha. The reconciliation reference workflow is end-to-end functional with 986 passing tests. Do not point closegate at real financial data without working through this page first.
Threat model
The six concrete threats closegate is designed to defend against:
- Prompt injection that issues a state-changing tool call.
Defense: server-side SoD + tier routing. The LLM never sets its own
actor identity; the gate denies on
source == llm AND actor.kind == llmregardless of prompt content. - Vendor bank-change request that the agent auto-confirms.
Defense:
always_human_accountsincludes vendor bank-change events. Forced HITL regardless of materiality. The 2:14am narrative on the blog covers this scenario in detail. - Intercompany clearing posted to the wrong entity.
Defense: per-entity materiality overrides + the
IntercompanyMatcherProtocol with explicit account-pair rules (1500-X ↔ 2500-X) that must match before auto-confirm. - Same-actor proposal-and-confirm (SoD violation).
Defense: the gate denies on
proposed_by == actor.id. This is enforced server-side; the MCP transport never acceptsactor_idas a tool parameter. - Audit-log tampering by an insider.
Defense: SQLite
BEFORE UPDATEandBEFORE DELETEtriggers enforce append-only at the database layer. Hash chain detects after-the-fact insertions.closegate-engine audit verifyexits non-zero on any inconsistency. - Stale FX rates producing wrong USD-equivalent matches.
Defense:
FxRateAdapterProtocol with explicitas_ofdates. The default fixed-rate FX is documented as a demo affordance; production deployments must wire a live adapter (ECB, OpenExchangeRates, Bloomberg). Multi-tz close-window overlap detection is surfaced in the UI.
What closegate is not designed to defend
- OS-level filesystem tampering. An attacker with root on
the host can mutate the SQLite file directly, bypassing the triggers. Mitigate
with cold-storage backup of
audit_eventsto an immutable store (S3 Object Lock, Glacier Vault Lock) on a schedule. - Compromised LLM API keys. If your
ANTHROPIC_API_KEYleaks, an attacker can run arbitrary agent loops. closegate's tier routing limits the blast radius (T2/T3 still need HITL with different actor identities), but key hygiene is your responsibility. - Side-channel attacks on the host. closegate is a
framework, not a hardened appliance. Run it inside your security-reviewed
perimeter; don't expose
:8001to the internet without a reverse proxy.
Go-live checklist for real financial data
- Replace any seed pack with real data via the M3 ingestion adapters (Stripe, QuickBooks, NetSuite, Codat, Merge, etc.)
- Provision real per-user actors via your IdP. Kill
human:demo-userby settingCLOSEGATE_AUTH_BACKEND=oidc+ OIDC env vars, or by running behind a reverse proxy that setsX-Actor-Idper authenticated user. - Pin
CLOSEGATE_AGENT_MODEL(e.g.claude-sonnet-4-6) for reproducibility. Update only on a model-validation pass. - Set up backups for
closegate-datavolume contents to immutable cold storage (S3 Object Lock or equivalent). - Put the engine MCP server behind a VPN / service mesh. The default compose file exposes
:8001to localhost-only for debugging; remove that in production. - Set
CLOSEGATE_ENV=prodto hide the dev banner. - Wire
FxRateAdapterto a live source (ECB daily, OpenExchangeRates paid tier, Bloomberg, in-house feed). - Configure
always_human_accountsfor cash, legal accruals, intercompany clearing, and your vendor-bank-change event types. - Run
closegate-engine validatein CI on every PR topolicy.yaml. - Wire the nightly
soc2-monitorCI job. Confirm it fails CI on regression.
Coordinated disclosure
Found a security issue? Do not open a GitHub issue. Email
the maintainer directly — see the contact in the SECURITY.md file in the repo.
We aim for a 5-business-day initial response, a 30-day fix window for
high-severity issues, and coordinated disclosure (we'll work with you on
public disclosure timing). The full disclosure timeline policy is in
SECURITY.md.
What we won't do
- Run a bug bounty. We're a small project; the operational cost outweighs the benefit pre-1.0.
- Promise CVE assignment. Depending on severity, we'll work with MITRE; not every fix gets a CVE.
- Disclose pre-patch. We coordinate with the reporter on timing, but won't publish vulnerability details before the patch ships.
Adjacent reading
- Compliance mappings — SOC 2, SOX 404, NIST AI RMF
SECURITY.mdin the repo — the full threat model + disclosure policy