Browsing & Verification
The plugin is the only Filament audit plugin built around cryptographic verification rather than a mutable activity feed.
Read-only by construction
The audit log resource is browse-and-view only. There are no Create or Edit pages, every mutation ability (canCreate/canEdit/canDelete/canDeleteAny/canReplicate) is hard-denied, and an EntryPolicy denies create/update/delete/restore/forceDelete/replicate for any caller - defence in depth behind core's own immutable entries.
Browsing
The Audit Log resource lists entries newest-first with columns for sequence #, recorded time, action, actor, subject, and verification status. Filters cover action, actor type, subject type, recorded date range, and verification state.
Actor and subject labels come from core's Chronicle::resolveReference() (honouring morph maps, no extra query - see Reference Resolution). Override per panel with ->labelResolver().
The detail view is a read-only infolist: Identity, Integrity (current/previous/payload hashes), Signature (algorithm, key id, signature read from the entry's checkpoint, shown as Unanchored when none), Payload, and a Decrypted section rendered through core's decrypted*() accessors with an erased-subject indicator (see Crypto-Shredding).
Verification
Verification is always deliberate - nothing verifies on a read or render path.
| Action | Where | Scope |
|---|---|---|
| Verify chain | Header action | The full ledger from genesis |
| Verify entry | Row action | A single entry |
| Verify segment | Bulk action | The selected span |
The Verify segment bulk action reduces the selected rows to a [min, max] sequence span and calls core's verifyEntryRange, which anchors on the enclosing signed checkpoints - never on a selected row's stored hash. See Scalable Verification.
Chain and segment verifies covering more than verification.queue_threshold entries (default 1000) are dispatched to the queue and notify you on completion.
Status badges and the health widget
Results are written to a plugin-owned, DB-backed store and surfaced as badges - Verified / Failed / Unverified / Stale (stale once newer entries are appended) - with a tooltip showing the last-verified time and decoded failure. Badge rendering is primed in a single query, so it stays N+1-free at volume.
The VerificationHealthWidget summarises the last-verified time, pass/fail, and a cheap checkpoint spine check (O(checkpoints)).
External anchoring
Since the plugin's v1.1, the panel surfaces core's external checkpoint anchoring. Like verification, anchor checks are deliberate and read-only - nothing contacts an anchor provider on a render path. All anchor surfaces are hidden unless core anchoring is enabled (they follow chronicle.anchoring.enabled; override with ->anchoring(true|false)).
The entry detail view gains a read-only External anchoring section listing the entry's checkpoint's anchors - per anchor the provider, a status badge, anchored_at, reference, and a copyable proof. It reads stored anchor status only, and degrades to Unanchored, No anchors, or Anchoring not configured.
Verification is exposed two ways, both gated by the same ->authorize() closure as the chain/entry/segment actions:
| Action | Where | Scope |
|---|---|---|
| Verify anchor | Row + detail header | One entry's checkpoint, via core's AnchorVerifier::checkpointHasValidAnchor() |
| Verify all anchors | List-page header | Every in-scope checkpoint, via AnchorVerifier::verify() |
"Verify all anchors" runs synchronously at or below anchoring.verify_all_queue_threshold (default 1000 checkpoints) and on the queue above it, notifying you on completion.
An Anchor badge column and matching filter read the checkpoint's stored anchor status (no provider call, no per-row query), and an AnchorCoverageWidget summarises coverage from cheap aggregates - checkpoints anchored vs total, plus pending/failed counts and the latest anchored_at.
To populate any of this, core anchoring must be configured - RFC 3161 TSA or the S3 Object Lock adapter. With none configured, every entry shows as Unanchored and the surfaces stay hidden.
Signing-key visibility
Since the plugin's v1.2, the panel surfaces signing-key rotation - which key signed each entry, drawn from its checkpoint. This is display-only: signature verification already happens inside chain/entry verification (the verifiers resolve each checkpoint's key through core's KeyRing), so these surfaces read key metadata only and never sign or verify. Toggle them with ->signingKeys(true|false) (default on).
The entry table gains a Signing key column - the entry's checkpoint.key_id as a state-coloured badge with the algorithm, and an Unsigned placeholder when the entry has no checkpoint. A matching filter lists the configured keys (labelled algorithm:keyId, the active one marked (active)) and narrows by key. The column reads the already eager-loaded checkpoint, so there's no per-row query.
Each key reads as one of three states:
| State | Meaning |
|---|---|
| Active | Signed by the key core is currently signing new checkpoints with |
| Retired | Signed by an earlier key - still kept in the ring to verify historical entries |
| Unsigned | The entry has no checkpoint yet |
The entry detail view badges the same Active/Retired state beside the key id (with a note that retired keys still verify historical artifacts), and a SigningKeyRingWidget on the list page summarises the ring: the active key, its size, the number of retired keys, and the active key's checkpoint coverage. See core's Signing & Keys and key rotation for how the ring is configured and rotated.
Crypto-shredding & GDPR erasure
Since the plugin's v1.3, the panel surfaces core's crypto-shredding state and can action a GDPR erasure. These surfaces appear only when core encryption is configured; toggle the read-only surfaces with ->cryptoShredding() (defaults to following core).
The read-only surfaces never unwrap a DEK or decrypt anything - they read the subject key's status only:
- An Erasure column (Encrypted / Erased / Not encrypted, with an On hold indicator) and filters by erasure state and legal hold. State is primed once per page, so the column stays query-flat with no per-row lookup.
- A Subject erasure detail section - state, wrapping
kek_id,erased_at, and active legal-hold status. An erased subject is shown as permanently unreadable while its entry stays intact and still verifies. - An Erasure proofs only preset filtering to the
subject.erasedproof entries, with requester and reason. - A
CryptoShreddingWidgetsummarising encrypted / erased / on-hold subjects and the active KEK.
The erase action
The panel's only write is an opt-in Erase subject (GDPR Article 17) action calling core's Chronicle::eraseSubject(). It does not mutate the ledger - core destroys the subject's DEK and appends a hash-chained subject.erased proof, leaving every existing entry (and its hash and signature) unchanged and still verifiable.
It is fenced deliberately, and each guard is enforced in the action's visibility and re-checked at execution:
| Guard | Behaviour |
|---|---|
| Off by default | Absent unless ->erasure(true) (config default false) |
| Authorized separately | ->eraseAuthorize() defaults to deny; never the verify gate |
| Confirmation | Type the exact subject_type:subject_id + a mandatory reason |
| Legal hold | Blocks by default; overridable only via ->eraseAllowHoldOverride() + a distinct checkbox |
| Idempotent | Re-erasing an already-shredded subject is a no-op |
Enabling requires both ->erasure(true) and an ->eraseAuthorize() closure that grants - the config flag alone never makes it reachable. See core's GDPR erasure and crypto-shredding for what erasure does to the key store and the audit trail.
Theming
The panel uses Filament's native CSS variables and utility classes only - no npm, no asset compilation, no required custom theme. It adopts your panel's primary color and dark mode automatically.
See also
- Installation
- Configuration
- Scalable Verification - the verification modes the actions map to
- External Anchoring - the anchors the panel surfaces
- S3 Object Lock Adapter - one way to produce anchors
- Signing & Keys - the key ring the panel surfaces, and key rotation
- Crypto-Shredding - the erasure state the panel surfaces
- GDPR Erasure - what the Erase-subject action performs