Evidence and seals
The Evidence and seals screen lets you verify the audit chain and its signed seals, seal a new range of events,
download an offline bundle, read the integrity alerts, decrypt the sensitive payload of one event with an audited reason
and run the retention of the evidence payloads. Portal path: /audit/evidence; its page title is Audit evidence.
It is used by the auditors (auditor), the security officers (security_officer) and the tenant administrators
(tenant_admin).
What the screen shows
Five tabs.
- Verification: section Evidence verification, "Chain, seals, signatures and timestamps.", with the buttons Verify signature and Offline bundle. Left: the badge Chain valid or Chain invalid and the answer of the verification, or "Not verified in this session."; the offline bundle when requested. Right: Audit gateway, the state of the audit gateway.
- Seals: table of the signed seals.
| Column | Content |
|---|---|
| Seal | Number of the seal. |
| From sequence | First event sequence covered. |
| To sequence | Last event sequence covered. |
| Events | Number of events sealed. |
| Merkle root | Merkle root of the range. |
| Key | Identifier of the signing key. |
| Sealed by | Author of the seal. |
| Sealed at | Date of the seal. |
- Integrity alerts: table Kind, Status, Detail (JSON), Created, with the filter Status (
open,acknowledged). Broken chains, gaps and invalid seals appear here. - Payload access: the form Decrypt a payload, "Sensitive payload of one event, audited with your reason.", then the table Access log (Actor, Action, Reason, At) where every evidence read is logged.
- Evidence retention: the form Evidence retention run, "Purge or erase payloads; held payloads are skipped. Start with a dry run.", then the section Evidence retention with the last retention and erasure runs and the payloads kept by a legal hold.
A click on a line of a table shows it in a side panel.
Actions
| Button or action | What it does | API call | Roles |
|---|---|---|---|
| Verify signature (tab Verification) | Verifies the whole chain, seals, signatures and timestamps. | GET /v1/evidence/verify | auditor, security_officer, tenant_admin |
| Offline bundle | Builds the bundle to verify the evidence offline and shows it. Shown to auditor and security_officer. | GET /v1/evidence/bundle | auditor, security_officer |
| Audit gateway panel | Reads the state of the audit gateway. | GET /v1/evidence/gateway | auditor, security_officer, tenant_admin |
| Tab Seals, Refresh | Reads the seals. | GET /v1/evidence/seals | auditor, security_officer, tenant_admin |
| Seal now then Create | Seals the events not sealed yet (empty form). Shown to auditor and security_officer. | POST /v1/evidence/seals | security_officer, auditor |
| Verify signature (row of a seal) | Verifies one seal and shows the answer. | GET /v1/evidence/seals/{id}/verify | auditor, security_officer, tenant_admin |
| Tab Integrity alerts, Status then Filter | Reads the alerts. | GET /v1/evidence/alerts | auditor, security_officer, tenant_admin |
| Decrypt a payload: Event, Reason, then Run | Browser confirmation "Decrypt a payload? This sensitive action is recorded in the audit log.", then shows the decrypted payload; the read is logged with the reason. | POST /v1/evidence/events/{id}/payload | auditor, security_officer (never platform_admin) |
| Table Access log | Reads the evidence reads. | GET /v1/evidence/access-log | auditor, security_officer |
Evidence retention run: Kind (retention, erasure), Cutoff (for example 2025-01-01T00:00:00Z), Events, Dry run (on by default), then Run | Browser confirmation, then purges or erases the payloads; held payloads are skipped; a dry run only reports. | POST /v1/evidence/retention/run | security_officer, tenant_admin |
| Section Evidence retention | Reads the last runs and the held payloads. | GET /v1/evidence/retention/evidence | auditor, security_officer, tenant_admin |
Roles
From services/control-plane/src/eai/domains/evidence/routes.py and common.py: the readers (READERS) are
auditor, security_officer and tenant_admin; sealing and the offline bundle need security_officer or auditor;
the access log needs auditor or security_officer; the retention run needs security_officer or tenant_admin. The
payload route checks PAYLOAD_ROLES (auditor, security_officer) explicitly: platform_admin, a technical
administrator, is not one of them. The portal hides the forms for the roles outside ROLES.sealWriters,
ROLES.payloadReaders and ROLES.retentionRunners of portal/src/lib/kit.ts and shows "Requires one of these roles:
..." instead.
An erasure run removes payloads for good. Always run it first with Dry run, read the report, then run it again without the dry run. Payloads under a legal hold are never purged.
Typical tasks
Prove the integrity of the audit trail
- Verify the chain. Tab Verification > Verify signature. Expected result: the badge Chain valid. If it fails: Chain invalid; open Integrity alerts to find the broken range.
- Seal the recent events. Tab Seals > Seal now > Create. Expected result: a new seal line with its
range and Merkle root. If it fails:
audit_nothing_to_seal(HTTP 409) when every event is already sealed;audit_seal_key_unavailable(HTTP 503) when the signing key cannot be reached. - Give the auditor an offline bundle. Tab Verification > Offline bundle. Expected result: the bundle
shown as JSON, to save and verify outside the platform. If it fails:
forbiddenwhen you are neitherauditornorsecurity_officer.
Read the sensitive payload of one event
- Fill the request. Tab Payload access: Event identifier and a Reason (for example the case number),
then Run and confirm. Expected result: the decrypted payload in Result. If it fails:
audit_payload_purged(HTTP 410) when the retention removed it;audit_payload_key_unavailable(HTTP 503) when the key cannot be reached;audit_payload_tampered(HTTP 409) when the payload no longer matches its hash. - Check the trace. Table Access log. Expected result: a line with your identity, the action and your reason. If it fails: click Refresh.
Purge the old payloads
- Run a dry run. Tab Evidence retention: Kind
retention, Cutoff, Dry run on, Run. Expected result: the report of what would be purged. If it fails:forbiddenwhen you are neithersecurity_officernortenant_admin. - Run for real. Same form with Dry run off. Expected result: the section Evidence retention shows the new run. If it fails: read the error under the form.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Chain invalid | A gap, a changed event or an invalid seal in the chain. | Read Integrity alerts and follow the audit evidence guide. |
audit_nothing_to_seal (HTTP 409) | No new event since the last seal. | Nothing to do. |
audit_seal_key_unavailable / audit_payload_key_unavailable (HTTP 503) | The signing or encryption key cannot be reached. | Check the key service of the platform, then retry. |
audit_payload_purged (HTTP 410) | The payload was removed by a retention or erasure run. | Only the event metadata remains. |
audit_payload_tampered (HTTP 409) | The stored payload no longer matches its hash. | Open an incident and keep the evidence. |
forbidden (HTTP 403) on Decrypt a payload as platform_admin | platform_admin is not a payload reader. | Ask an auditor or a security_officer. |