Audit
Declare the catalogue of audit events your app emits — the vocabulary a SOC 2 reviewer sees at publish time, written to a tamper-evident, hash-chained trail.
Declare every audit event your app emits
Anything your app records to the platform's tamper-evident audit trail — a claim, a decision, a role change, a GDPR erasure — is written by calling AuditClient.log(action="...", ...) from the egav-audit-client package. The audit block in app.manifest.yaml is the declarative catalogue of those actions: one entry per distinct action code your app ever passes to log(). It declares nothing at runtime and grants nothing — it is the published vocabulary a SOC 2 or 21 CFR Part 11 reviewer reads to see, at a glance, every kind of event your app can produce, without grepping the source.
The block sits at spec.audit.event_types[]. Each entry describes one action code.
| Key | Required | Purpose |
|---|---|---|
code | yes | Dotted action code (^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$ — at least one dot, e.g. automation.task.assigned). Must match the action argument passed to AuditClient.log(). |
description | yes | What triggers the event and why a SOC 2 / 21 CFR Part 11 reviewer cares — the human-readable audit vocabulary. |
actor_type | no (default user) | Who initiates it: user (an authenticated user via a route handler), system (the platform itself — a sweeper, dispatcher, or lifecycle hook), or either. Compliance tooling uses this to separate operator activity from autonomic actions. |
metadata_keys | no | The keys your app may place in the event's metadata dict. Documentation only — see below. |
audit:
event_types:
- code: automation.task.assigned
description: A user / role / group was added as a subject of a HITL task at
task creation. One event per resolved subject.
actor_type: system
metadata_keys:
- task_guid
- subject_user_guid
- relation_code
- code: automation.task.claimed
description: A user took the active claim on a HITL task. Rotates the claim
token and sets claim_expires_at.
actor_type: user
metadata_keys:
- task_guid
- lease_seconds
- code: automation.task.claim_expired
description: Claim lease elapsed without a decision; the sweeper auto-released
the claim. Task returns to PENDING.
actor_type: system
metadata_keys:
- task_guid
- prior_claimantAbsent, empty, and present mean three different things
The block is optional, and each of its three states carries a distinct meaning:
- No
auditblock — the app does not directly emit audit events. Silence, not an assertion. - Present but
event_types: []— a deliberate zero events signal. The app has been reviewed and confirmed to emit nothing directly. - Populated — the full vocabulary is published. Control Plane can later validate that your
log()call-sites match the declared codes, so the catalogue a reviewer sees at publish time cannot silently drift from the code.
metadata_keys documents the payload, it does not validate it
metadata_keys is a promise to reviewers, not a contract the platform enforces. The audit service stores whatever metadata dict you pass to log() as-is; it does not schema-validate the payload against the declared keys. List the keys you intend to attach so a reviewer knows what context each event carries — and keep the list and the code in step yourself. Keep the values to GUIDs and codes, never resolved names or emails: the audit trail persists identifiers only, never PII.
The trail behind the catalogue
This page covers the manifest declaration. The trail itself — owned by the egav-audit service — is append-only and hash-chained (each row linked to its predecessor by SHA-256), so any tampering is detectable, and it stores actor GUIDs rather than names or emails, with the display layer resolving GUID to name at render time. Keep your metadata free of personal data anyway: the chain cannot be edited in place. When a person is erased, the audit events about that person are deleted and one GDPR_ERASE event is appended to each affected chain, recording which sequence ranges were removed and how many events (never the person), so the chain still verifies and every gap is accounted for. Copies in archived retention windows and exports already handed out are not reached. See GDPR & compliance for the erasure cascade, and the app manifest for how the audit block fits alongside the rest of spec.