Skip to content

Type to search pages.

View .md

Compliance packs

Requires an enterprise license with the compliance_packs feature, and the enterprise binary built from ee/.

Terminal window
af compliance soc2 --org acme
af compliance hipaa --org acme --months 12 --output json --out evidence.json

It is not an audit report and it is not an opinion. It is a document that says what this system recorded, names the artifact so somebody can go and look, and leaves every conclusion to the person whose job that is.

The four outcomes, three of which are not “pass”

Section titled “The four outcomes, three of which are not “pass””

Evidenced. The check ran, the artifact exists, and it says what the control asks about. The artifact is named.

Not evidenced. The check ran and there was nothing to show. This is the ordinary state of a new installation and it is not a failure. Most controls are here on the first day.

Failed. The check found evidence that the control is not holding: an audit chain with a break in it, a golden published without a clean scan, a membership removal that did not revoke the member’s sessions.

Outside this product. The control is real and nothing here can speak to it: physical security, background checks, a backup plan. Listed rather than quietly omitted, because you need the whole framework and you need to know which parts to go and get from somewhere else. A pack that showed only the controls it happens to cover would read as a complete answer and would be about a third of one.

Every control also says what this product covers of the requirement, which is never all of it.

Code Meaning
0 A report was produced.
6 A control has evidence of not holding.
3 A configuration problem: no organisation, an unknown pack, no licence.

Exit 6 is what a nightly job watches, so a broken audit chain stops a pipeline without anybody having to parse the document. Controls that are merely not evidenced do not fail the command.

The audit log, recomputed rather than believed. Each entry carries the hash of the one before it, so altering or removing an entry leaves a break. Reading the stored hash and comparing it to itself would pass on a rewritten log, which is the only log where it matters, so every hash is recomputed from the entry’s contents.

Sequence gaps are reported and are not proof of tampering: the sequence comes from a database sequence, and a rolled back transaction consumes a number without writing a row. A deletion looks the same. The report says so rather than choosing an interpretation.

The masking attestations, signature checked before anything they say is repeated. An attestation is a signed statement that a golden was scanned and found clean, and a report that repeated an altered one would launder it into evidence.

The privileges on the audit log, read from the database rather than assumed from a migration. The application role should hold INSERT and SELECT and nothing else, so a rewrite is refused by the database rather than by a code path somebody can forget to call. A grant of UPDATE, DELETE or TRUNCATE is reported as failed.

Row level security, on every table holding tenant data, found by looking for an org_id column in the catalogue rather than from a list in the source. A table added next year and forgotten is exactly the table this has to notice. A role holding BYPASSRLS is reported as failed, because it makes every policy decorative.

Environments and goldens, for whether a copy of production-shaped data was created from an unverified golden or left behind after teardown.

Every golden is scanned for real data before it can be branched, and the scan is signed with what was looked at, how many rows were sampled, and the hash of the rules used. A scan is a sample and not a proof. It is evidence that a masking rule was applied and worked on what was read. It is not an expert determination under 164.514(b)(1), and if you need one, this is an input to it rather than a substitute for it.

Terminal window
export AF_CONTROL_PLANE_DATABASE_URL=postgres://reader@control-plane/antifailure
export AF_APP_ROLE=antifailure_app # the role the APPLICATION connects as
export AF_AUDIT_RETENTION_DAYS=2555 # 0 means entries are never pruned

Run this as a role that can SELECT and nothing else.

AF_APP_ROLE is the role the application connects as, whose privileges on the audit log are one of the things reported on. It is not the role this command connects as.

Retention is read from configuration rather than from the database, because a retention policy that has not yet deleted anything leaves no trace in the data.

Evidence that could not be read is named at the top of the document and on the control it belonged to:

> **This report is partial.** Some evidence could not be read, so the controls
> below rest on less than the full period:
>
> - masking-attestations: the golden versions could not be read: permission denied

A control reported as “not evidenced” because a query failed must not be mistaken for one where there was genuinely nothing to show.

Every claim on this page is checked on every pull request, against a real Postgres rather than a fixture.

The suite creates a database of its own, applies every control plane migration with the control plane’s own runner, and appends every audit entry through appendAudit, which is the implementation that wrote every hash in your installation. The Go verifier that recomputes those hashes is therefore checked against a chain it did not write; a verifier checked against its own output agrees with itself, and a disagreement of one byte would report every clean audit log as tampered.

It then runs both packs and requires four things to be reported, each with the break restored afterwards so no case depends on running before another:

  • an entry altered with a privileged connection, named at its own sequence number;
  • an entry deleted with one, named as a broken link at the entry that followed it;
  • a table carrying an org_id with row level security switched off, named in the failing control, and no longer named once it is switched on;
  • an application role granted UPDATE on the audit log, naming the privilege.

The number of tables carrying an org_id is never asserted; what is checked is that none of them has row level security disabled.

The reports that run produces, and a note saying what it did not check, are kept as a build artifact. Run it yourself with just compliance.