Conformiti
ProductEditionsPricing ConsultingSelf-hostDocs Security GitHub Book a demo

Security & trust

A compliance tool has to
be worth auditing itself.

You are trusting it with your policies, your access reviews and your auditor's evidence. So the source is public, every review is on record, and the residual risks are named.

Posture at a glance

LicenceAGPL-3.0. Every line readable.
TelemetryNone. The only call you did not configure is the interface's two Google webfonts, which you can self-host.
Second factorTOTP and passkeys, with backup codes
Single sign-onOIDC and SAML 2.0, from the environment only
TransportHttpOnly cookies, __Host- prefixes over https
Evidence integritySHA-256 per file, Ed25519-signed manifest
Audit trailServer-side only. The API cannot edit it.
UploadsCapped at 32 MB, active content refused, served sandboxed, optional ClamAV

How it is built

The decisions that matter.

Authentication fails closed

A passkey that looks cloned is disabled and the sign-in refused, never dropped to password-only. Backup codes belong to the account, so recovery still works.

Tokens rotate and can be revoked

Refresh rotates and blacklists; sign-out revokes. Logins are throttled per client and shared across workers in Docker. It is rate limiting, not account lockout.

SSO cannot be configured from a form

Environment only. External identities never link to an administrator, and SSO_STEP_UP asks for a local factor when the provider asserted none.

Authorisation is resolved server-side

Folder grants gate folders and documents. They do not narrow the registers: any signed-in account reads the controls, risks and vendors, so the Auditor role reaches further than its name suggests. Narrowing it is on the list.

Tenancy is enforced at the ORM

A view that forgets to filter still reads one workspace. Not absolute: sliced or combined querysets skip the filter, and a request with no workspace gets a 403.

Evidence is rendered, never executed

Byte-checked before display. PDFs drawn by pdf.js, Office files rendered as structure, never as the file's own markup.

The audit trail records names, not values

Field names only, with password, token and code keys dropped outright.

Outbound calls are hardened

Jira is https-only, public hosts only, no redirects. Webhooks are https and operator-set. The TOTP seed and Jira token are encrypted per field.

The Django admin at /admin/ sits outside all of these rules: no second factor, no throttle. Restrict who can reach it.

Evidence integrity

What a sealed package proves,
and what it does not.

The manifest is signed with Ed25519 by a key kept outside the database. The fingerprint is published at /api/signing-keys/.

The bundled verifier is standard library only, tested against the RFC 8032 vectors, so it runs on an air-gapped laptop. It checks the signature and every listed digest. It does not prove the bundle is complete.

The honest limit. The signature covers manifest.json: the snapshot and the evidence digests. The auditor's conclusions are recorded after sealing and are not signed yet. A signature also cannot prove the key was never stolen, or when it signed. A digest you send the assessor out of band closes that gap.
what the bundle contains
conformiti-package-fy26-soc2/
├── manifest.json      the signed bytes: snapshots + digests
├── MANIFEST.sha256    the manifest's digest, on its own
├── manifest.sig       detached Ed25519 signature
├── signing-key.pub    the public key, for comparison
├── SHA256SUMS         every file, hashed
├── verify.py          stdlib only, no network
├── controls.csv       scope, status, owner, conclusions
├── evidence.csv       name, version, size, digest
├── samples.csv        population, selections, verdicts
├── trail.csv          what happened, and when
├── INTEGRITY.txt      the re-hash result at export time
├── README.txt         how to verify it, and what that proves
└── evidence/          the files themselves

Check it with sha256sum -c SHA256SUMS and python3 verify.py.

Quality gates

What we run,
and what we publish.

A pipeline that fails the build. ./install.sh --test runs the local half on your own machine.

Continuous integration gates
GateWhat it proves
tools/validate.py
20 checks, no dependencies
Routes, the SPA-to-API contract, and a migration for every model change.
manage.py test
1,251 tests
Auth, MFA, RBAC, evidence access, the auditor's reach, import safety, the audit trail, WebAuthn, SAML and Ed25519 against RFC 8032.
Backend matrixPython 3.11 to 3.14 on SQLite, plus PostgreSQL 16.
FrontendA production build and npm audit --audit-level=high.
DockerBoth images build, and the API boots healthy.
End-to-end
217 Playwright tests
A real browser drives the built app under both auth transports, failing on any console error.

Every review is on record

SECURITY.md lists thirteen reviews, eight of them independent. REVIEWS.md records each finding and the release that fixed it.

Dependencies are current

Django 5.2 LTS, React 19, Vite 8, pdf.js 6. Dependabot bumps go through review, never merged blind.

What is still open

Three outside findings are deferred, each with its reasoning beside it. The rest of the list is deployment risk, named in SECURITY.md and yours to close.

Coordinated disclosure

Found something?
Tell us properly.

Acknowledged within two business days, then triaged with a severity and a target date.

  • Email sales@conformiti.app with SECURITY in the subject, or open a private advisory on GitHub
  • Include a proof of concept and the version from /api/health/
  • No public issues, and no testing against someone else's deployment
  • Fixes ship with a release note, crediting you unless you ask us not to

No bug bounty. We would rather promise a fast, honest answer than a payout we cannot fund.

In scope

  • The application and images on a supported release
  • Anything that crosses a workspace, folder-grant or package boundary
  • Authentication, MFA, SSO and sessions
  • Evidence delivery, the viewer and uploads
  • Manifest, signature or bundle integrity
  • This website

Out of scope

  • Anything needing a compromised administrator (but do tell us about /admin/)
  • Missing headers on a deployment you configured
  • Brute-force reports against the demo dataset
  • Scanner output with no demonstrated impact
  • Social engineering of us, our clients or their auditors

Managed hosting

If we hold your data.

We recommend self-hosting. If you ask us to run it, this is what you rely on.

Managed-service commitments
AreaCommitment
RegionCanada or the United States, never moved without written instruction.
EncryptionTLS 1.2+ in transit, encrypted disks, field-level encryption for secrets.
IsolationA single-tenant instance and database, never a shared schema.
BackupsNightly, encrypted, kept 90 days, with tested restores.
Signing keysYour instance's own key, or one you supply from your own secret store.
Access by usNamed staff, second factor, every action in your audit log under our name.
Sub-processorsListed in the DPA, with notice and a right to object.
ExitA full export on demand. Data destroyed 60 days after termination.
IncidentsNotified within 72 hours of confirming a personal-data breach.

A DPA on every managed engagement, and a BAA where you need one. Ask on the contact form.

Do not take our word for it.

Read the source, run the gates, seal a package and verify it yourself.