Attestkeep docs

Compliance frameworks

A framework mapping turns what the cluster did into what an auditor asked for. It does not turn either one into a certification.

What an evidence package contains

For a period you choose:

Depending on what the installation has switched on, the document also carries a provenance record for images whose build provenance was verified, a vex section listing the findings a trusted producer's statement suppressed and the words they used, a runtime section of the events your own Falco delivered and what your runtime rules decided about them, an issues section of the tracker issues opened and closed for critical findings, and a context section saying whether admission escalated on where the pod was going to run.

The allows matter as much as the denials. Evidence that only lists blocks can show that something was blocked; it cannot show that a control was operating.

Reading report.json

Only report.json, and the PDF named beside it, is signed; the PDF renders the same data and never adds to it. A package keeps the shape it was generated with: one generated before a field existed does not have it, is never regenerated, and reads exactly as it did. Treat a missing field as "this package predates it", not as an error. The fields below arrived in 1.2.0.

Requirements: status, result, tests

Every entry of framework.requirements[] keeps status (met, partial, gap, outside_scope), note and evidence[], and adds two fields in OSCAL's words, derived from status:

FieldValuesFrom status
resultsatisfied, not-satisfied, not-applicablemet → satisfied; partial, gap → not-satisfied; outside_scope → not-applicable
implementation_statusimplemented, partial, not-implemented, not-applicablemet → implemented; partial → partial; gap → not-implemented; outside_scope → not-applicable

framework.summary counts satisfied, not_satisfied and not_applicable beside the four status counts. tests[] lists what answered the requirement, one entry each:

FieldMeaning
id, titleThe attestation.
methodTEST when it reads a record of what happened, EXAMINE when it reads a configuration.
resultThe test's own result, in the words above.
observationThe attestation's note, with the period's numbers.
evidence[]{path, type}: a path into this report and its evidence type — Report, Configuration, Audit Record or Log.
sourceWhere it was read from: scan records, the admission ledger, the audit log, triage records, issue history, runtime events, availability records, the reconciliation sweep, policy or installation configuration, or the scope of this document.
collected_atWhen that source was read: the newest scan for scan records, the closing sweep for the reconciliation sweep, the generation time for everything read live. Empty when there is no such moment; never invented.

Every requirement carries at least one test. The SOC 2 and EU CRA/NIS2 requirements are written as their own assessments and each is its own test. An outside-scope requirement carries one EXAMINE test, not-applicable, with the reason as its observation; a clause with a limb no record speaks to carries that limb as one more EXAMINE test, not-satisfied, because it is what holds the clause below met. The PDF prints the tests beneath each requirement.

The admission population

admission.records[] is every admission review of the period, one row each: at, namespace, pod, image, digest, allowed, reason, policy_name, policy_hash, break_glass, cached, requester. There is no cap — a population that stops at a limit is a sample — and admission.records_truncated is always false. The grouped denials and the chain[] DEPLOY rows stay as the readable summary; records is what a sample is drawn from. The PDF prints how many rows there are and where they are, not the rows.

How the document was produced

generation states when and by what the document was generated (generated_at, generator), the source_system, the query period — end exclusive, timezone: UTC — and queries[]: for each section, the query behind it in words and the number of rows it returned. The two capped queries say whether they hit their cap: runtime.decisions at 500 and, when asked for, runtime.raw_events at 5000. actors gives the actor scheme and unresolved_rows, described below.

Runtime clocks

WhereFalco's clockOperator's clock
runtime.decisions[]event_timereceived_at
runtime.raw_events[]event_timereceived_at
runtime.coverage—last_event_at, longest_silence_seconds, silence_measured_from; clock says so

event_time is the time field of the Falco event, and the one to argue from when a row is compared with the detector's own log. Coverage measures the feed reaching the operator, which only the operator's clock can show. The rule changes, triage releases and integration changes of the period are in runtime.audit_entries[], each with its kind.

Operator declarations

organisation.declarations carries time_source and retention_policy, the operator's own statements entered under Settings > Organisation > Operator declarations. The product does not measure clock synchronisation or enforce a retention period, and the PDF labels both lines "declared by the operator". Empty when nothing was declared. No framework clause reads them.

Personal data in evidence packages

An evidence package leaves the operator's hands. From 1.2.0 it names accounts, never people.

Console accounts are written by id. Every person field is user:<account id>: the predicate's generated_by (and so the README's "Generated … by" line), chain[].actor for audit-log rows, triage[].triaged_by and runtime.audit_entries[].actor. The console's audit log still shows names: it is the operator's own record, and it is where the operator resolves an id to a person when an auditor asks. system (a sweep) and anonymous (a request with no session) are not people and are written as they are.

Rows recorded before 1.2.0 hold only a name. At generation such a row is matched, by exact display name or by e-mail without case, against today's accounts, disabled ones included; if exactly one account matches, the row is written as that account's id. Otherwise it is written user:unresolved and counted in generation.actors.unresolved_rows, and the README says how many. Nothing is guessed, and the stored rows are not back-filled.

Accounts are disabled, not deleted. Deleting a user disables the account: it cannot sign in, its sessions end, and its id and name stay, so every user:<id> in an issued package still resolves. For an erasure request an administrator can then Anonymise the disabled account. In one transaction that blanks its name, e-mail and avatar and rewrites every copy of its name in the audit log, triage decisions, and runtime policies and their revisions to user:<id>. Where another account bears the same display name, a row matched by name alone could be either person's and is left as it is. The response and the audit entry state how many rows were rewritten and how many were left. The rows themselves stay: an erasure request does not delete the security log, it stops it naming the person. Packages signed before the anonymisation keep what they were signed with, and comments already posted to an external issue tracker are not rewritten.

The cluster copy of a triage decision names the account. The triagedBy of an ImageSecurityReport triage entry is user:<account id>: the report is a cluster object that no anonymisation reaches, so no name is written there. Entries written before 1.2.0 hold a display name and are not patched; an entry written by the kubectl plugin leaves the field empty.

Kubernetes identities are pseudonymised. admission.records[].requester is written as the API server gave it when it is a system: identity — service accounts, controllers. Any other username, an OIDC e-mail or a certificate CN, is a person, and is written k8s-user:<first 16 hex characters of the SHA-256 of the username>. The raw username stays in the admission ledger, where the operator resolves the pseudonym on request.

Free text is not rewritten. Break-glass approvers, triage justifications, block release notes and audit-log details go into the package as they were typed. The console asks for a ticket or a role rather than a person's name under each of those fields; what is typed there is the operator's choice.

Deliveries name the package, not the recipient. Delivering or e-mailing a package from the console writes an audit entry whose target is the package; the recipient's address goes only into the entry's detail, which packages do not copy. Entries written before 1.2.0 carried the address as their target, and a package covering that period still shows it in chain[].

Contact fields are the organisation's. organisation.contact_email and security_contact are there because the Cyber Resilience Act asks for a reachable contact. Use a role mailbox, not a person's address.

Old packages are unchanged. A package generated before 1.2.0 was signed with names in it and keeps them.

Mapped frameworks

Thirteen, and 293 clauses between them.

FrameworkInstrument and editionClausesWhat the mapping reaches
EU Cyber Resilience Act and NIS2Regulation (EU) 2024/2847; Directive (EU) 2022/255534CRA Annex I Part I (essential cybersecurity requirements) and Part II (vulnerability handling), with NIS2 Article 21(2) risk-management measures.
DORARegulation (EU) 2022/255422Chapter II Articles 8 to 11 (identification, protection and prevention, detection, response) and Chapter V Article 28 (ICT third-party risk), for EU financial entities.
GDPR Article 32Regulation (EU) 2016/6796Article 32, security of processing.
NIST SP 800-190Application Container Security Guide15Section 3 risks and section 4 countermeasures for images, registries, orchestrators and containers.
NIST SP 800-218 (SSDF v1.1)Secure Software Development Framework v1.113PO.3 and PO.5, PS.1 to PS.3, PW.4, and RV.1 to RV.2 — the practices that reach past the build into what is deployed.
NIST SP 800-53 Rev. 5Security and Privacy Controls for Information Systems15Selected CM, RA, SI, SR, AU and CP controls.
SOC 2AICPA TSP section 100 (2017, revised 2022)13Common Criteria CC6 to CC9 and Availability A1. US in origin, widely used elsewhere.
ISO/IEC 27001:2022Information security management systems — Requirements12Annex A controls 5.15, 5.23, 5.25, 5.30, 8.8, 8.9, 8.15, 8.19, 8.28 and 8.32.
PCI DSS v4.0.1PCI Security Standards Council, version 4.0.1 (June 2024)13Requirements 6.3 and 6.5 (vulnerability management and change control), 10.2 (audit logs) and 11.3 (internal vulnerability scanning), for the images a cluster admitted.
MAS Technology Risk Management GuidelinesMonetary Authority of Singapore, 18 January 202130Sections 3.3, 6.1 and 6.3, 7.2 to 7.6, 11.3 and 11.4, 12.1 and 12.2, 13.1 and 13.6, and 15 — supervisory expectations addressed to a financial institution, not a standard anybody is certified against.
ASD Essential Eight Maturity ModelAustralian Signals Directorate, November 202346Appendices A to C: patch applications, patch operating systems and application control, at Maturity Levels One, Two and Three.
Control Criteria of ISMAPISMAP Steering Committee, Japanese edition of 1 August 202442The controls criteria of attached table 3 that a cluster's admission control and image scanning can answer: asset inventory, change management, logging, control of operational software, technical vulnerability management, acceptance criteria, ICT supply chain and technical compliance review.
ISMS-P Certification CriteriaPIPC and MSIT joint notification Annex 7, as explained in KISA's guide of November 202332The criteria of domains 1 and 2 that a Korean organisation's cluster records can answer: asset identification, outsider supervision, authentication and privileged accounts, development and hand-over, change management, logs, security systems and cloud, patch management, and incident prevention and response.

The clause count is what the operator assesses, not what the instrument contains: a clause a cluster cannot answer is reported as outside scope with the reason, and those are included in the count above. NIS2 is mapped inside the CRA package because the two bind different subjects — the manufacturer and the entity — over the same evidence.

Community includes one framework, and it is yours to choose rather than a fixed one. A company doing only ISO 27001 should not be asked to pay for a SOC 2 mapping it will never open.

Where a translation stands between you and the text

Two of the mappings are read from a text that is not published in English, and they say so in every clause rather than in a footnote.

ISMAP. An English reference translation of the Control Criteria exists, but where its entry only points at ISO/IEC 27002:2013 the obligation is translated from the in-force Japanese edition of 1 August 2024, and the citation says which. Every clause also states whether the control it maps is one the programme requires a provider to implement — the basic statement requirement — because an assessor reads that distinction first.

ISMS-P. There is no official English text of the criteria at all. Every clause is our translation of the Korean criterion sentence as printed in KISA's guide, and each citation says which criterion it came from. Because an ISMS-P criterion states a technical measure and a procedure, a designation or a periodic review together, no criterion is reported as fully met: a cluster's records answer the first half only, and the mapping reports partial by design.

What this does not claim

A mapping is an argument, not a verdict. It says: this control text asks for evidence of X, and here is what your cluster did about X. Whether your auditor accepts it is between you and your auditor, and no tool can promise otherwise.

Container images are also one slice of any of these frameworks. None of them is satisfied by what happens at admission alone, and a product that implied otherwise would be selling a false finish line.

Each mapping names the clauses a cluster cannot answer and marks them outside scope with the reason, so a reader holding the instrument finds them rather than an absence. What that means for the five newest:

Where the mappings come from

Each mapping is read from the published text of the framework and cites the clause it maps to, so you can check the reading rather than trust it. Where a clause is genuinely ambiguous about containers, the mapping says so instead of picking the flattering interpretation.