Security

Security posture.

The threat model, go-live checklist, and disclosure policy. Don't point closegate at real financial data without this page.

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:

  1. 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 == llm regardless of prompt content.
  2. Vendor bank-change request that the agent auto-confirms. Defense: always_human_accounts includes vendor bank-change events. Forced HITL regardless of materiality. The 2:14am narrative on the blog covers this scenario in detail.
  3. Intercompany clearing posted to the wrong entity. Defense: per-entity materiality overrides + the IntercompanyMatcher Protocol with explicit account-pair rules (1500-X ↔ 2500-X) that must match before auto-confirm.
  4. 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 accepts actor_id as a tool parameter.
  5. Audit-log tampering by an insider. Defense: SQLite BEFORE UPDATE and BEFORE DELETE triggers enforce append-only at the database layer. Hash chain detects after-the-fact insertions. closegate-engine audit verify exits non-zero on any inconsistency.
  6. Stale FX rates producing wrong USD-equivalent matches. Defense: FxRateAdapter Protocol with explicit as_of dates. 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_events to an immutable store (S3 Object Lock, Glacier Vault Lock) on a schedule.
  • Compromised LLM API keys. If your ANTHROPIC_API_KEY leaks, 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 :8001 to the internet without a reverse proxy.

Go-live checklist for real financial data

  1. Replace any seed pack with real data via the M3 ingestion adapters (Stripe, QuickBooks, NetSuite, Codat, Merge, etc.)
  2. Provision real per-user actors via your IdP. Kill human:demo-user by setting CLOSEGATE_AUTH_BACKEND=oidc + OIDC env vars, or by running behind a reverse proxy that sets X-Actor-Id per authenticated user.
  3. Pin CLOSEGATE_AGENT_MODEL (e.g. claude-sonnet-4-6) for reproducibility. Update only on a model-validation pass.
  4. Set up backups for closegate-data volume contents to immutable cold storage (S3 Object Lock or equivalent).
  5. Put the engine MCP server behind a VPN / service mesh. The default compose file exposes :8001 to localhost-only for debugging; remove that in production.
  6. Set CLOSEGATE_ENV=prod to hide the dev banner.
  7. Wire FxRateAdapter to a live source (ECB daily, OpenExchangeRates paid tier, Bloomberg, in-house feed).
  8. Configure always_human_accounts for cash, legal accruals, intercompany clearing, and your vendor-bank-change event types.
  9. Run closegate-engine validate in CI on every PR to policy.yaml.
  10. Wire the nightly soc2-monitor CI 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

Inbound

Talk to the maintainer

Two design-partner slots open this quarter. One real workflow, your real policy.yaml, monthly 30-min call, direct line. Apache-2.0, self-hosted, no seat licensing — forever.