Compliance evidence pack
What a security review can verify about this codebase today, where the evidence lives, and — the part most vendor documentation omits — what is not covered.
The distinction that matters throughout: some of this is engineering, and some of it is certification. SOC 2 Type II, ISO 27001 and a third-party penetration test are audit activities performed on an operating organisation. No amount of code produces them, and a vendor implying otherwise is telling you something false. Everything in the first table is code and is verifiable from this repository; everything in the second is not and says so.
Verifiable from the repository
| Control | Evidence | Where |
|---|---|---|
| Software bill of materials | CycloneDX SBOM generated by syft on every release and image build | release.yml, publish-images.yml |
| Artifact signing | Images and release artifacts signed with cosign (keyless, OIDC) | publish-images.yml |
| Build provenance | SLSA provenance attestation attached to each image | publish-images.yml |
| Immutable audit trail | prev_hash / entry_hash chain; audit rows reject UPDATE and DELETE by the app role | services/api/app/middleware/audit_middleware.py, services/api/app/services/audit_hash.py |
| Credential encryption at rest | Fernet (vault:v1) by default; per-secret DEKs wrapped by a KMS-held KEK (vault:v2) when enabled | services/api/app/security/credential_vault.py |
| Backup encryption | AES-256-GCM with a SHA-256 manifest; restore refuses a tampered or truncated archive | scripts/backup_crypt.py, gated in integration.yml |
| Disaster recovery | Seed, back up, drop the schema, restore, assert the row count — plus a bit-flip that must be refused | integration.yml backup-restore job |
| Tenant isolation | Enforced at the query layer in Postgres, ClickHouse, Neo4j, Redis, Kafka and Qdrant; offline assertions plus a live two-tenant replay | tests/isolation/, isolation-live.yml |
| Tenant deletion | Erases across all five stores; the Postgres table list is discovered from information_schema so a new table cannot be missed | services/api/app/services/tenant_deletion.py |
| Data retention | Per-tenant policy with a purge worker; the gate asserts rows are actually deleted | services/api/app/workers/retention_purge.py |
| Static analysis | CodeQL across Python, Go and JavaScript/TypeScript on every PR | codeql.yml |
| Dependency scanning | Dependabot alerts enabled; security_audit.py fails closed on an unscannable service | security-audit.yml |
| Secret scanning | gitleaks on every PR | security.yml |
| Container and IaC scanning | Trivy, checkov, tfsec | security.yml |
| Prompt-injection resistance | Three adversarial corpora: 32 payloads scanned in isolation and 71 incident pairs whose payloads are placed in attacker-controllable fields, both against a clean twin with a recall floor and a false-positive ceiling, plus 34 held-out pairs authored after the guard was frozen and carrying no floor at all, on which the guard measures 7.1% | services/agents/tests/adversarial/ |
| Claim-to-gate traceability | Every product claim mapped to the CI job that fails when it stops being true | docs/audit/CLAIM_TO_GATE_MATRIX.md |
The last row is the one worth reading first. It is the honest index: 290 rows
GATED, 0 PARTIAL, and a ratchet that refuses to let a row sit at
NO GATE. Re-read the figure from the table rather than from here — a count
copied into prose goes stale in silence, which is why
scripts/readme_gates.py cross-checks this sentence against the matrix.
Not covered, and not claimed
| Status | What it would take | |
|---|---|---|
| SOC 2 Type II | Not held | An observation period against a named auditor, evidencing operating effectiveness of controls — an organisational activity |
| ISO 27001 | Not held | A certified ISMS, likewise organisational |
| Third-party penetration test | Not commissioned | An engagement and a remediation cycle. CodeQL and the security workflow are static analysis, which is a different assurance |
| FIPS 140-3 validated cryptography | Not validated | See FIPS posture |
| Customer-managed keys | Partial | vault:v2 wraps per-secret DEKs with a KEK in AWS KMS, so a customer-held CMK is supported for connector credentials. Backup encryption uses a static key, not a CMK |
| Data residency guarantees | Self-hosted only | The deployment runs where you run it. No managed offering makes a residency commitment |
| Continuous control monitoring | Not implemented | The CI gates prove the control exists in the build; nothing continuously attests a running deployment |
Producing the evidence
# Claim-to-gate summary and per-row status
python3 scripts/check_claim_gate_matrix.py
# Deployment-state snapshot, redacted and safe to attach
python3 scripts/support_bundle.py --out bundle.json
SBOMs and signatures are attached to each GitHub release; verify a signature
with cosign verify against the keyless OIDC identity, and note that a
signature proves the artifact came from this repository's workflow — not
that the contents are free of defects.
How to read this honestly
The controls above are real and the gates are real, and neither is a substitute for an audit. If a questionnaire asks "are you SOC 2 compliant", the answer is no. If it asks "can you evidence that credentials are encrypted at rest, that a backup restores, and that one tenant cannot read another's data", the answer is yes, and the evidence is a CI job you can read rather than a claim in a PDF.
That is a narrower claim than most vendors make and a wider one than most can show.