/v1/governance:
- Actor mappings: link opaque actor IDs to PII, with pseudonymize and delete operations.
- Archives: list completed audit-log archives and download archive objects from object storage.
- Audit logs: immutable, hash-chained history with a read-only integrity check.
AUTH_PROVIDER=plugin-auth in multi-tenant mode, tenant identity comes from the JWT. Matcher rejects startup when MULTI_TENANT_ENABLED=true and PLUGIN_AUTH_ENABLED=false.
Every governance route stays inside the caller’s tenant. Actor-mapping reads have two authorization tiers: the list, which omits
displayName and email, and the single-record de-anonymization read. This separation keeps identity resolution apart from browse access.Actor mappings
An actor mapping links an opaque
actorId (for example user:550e8400-e29b-41d4-a716-446655440000) to human-readable PII (displayName, email). Outside local, development, and test environments, set ACTOR_PII_ENCRYPTION_KEY to a base64-encoded 32-byte key before you use actor mappings. If you leave it unset, Matcher continues to run. PII-bearing mapping operations (upsert, single-record read, and pseudonymization) then return an encryptor-required error. The PII-free list and delete paths need no encryptor. Matcher never stores mapping PII in plaintext.
List rows omit the mapping PII fields (displayName, email) by design, but they do return the actorId itself. On upsert, Matcher trims leading and trailing whitespace and rejects empty or whitespace-only IDs and IDs longer than 255 characters. Matcher does not impose an opaque-ID format and does not redact the value. An actorId that itself contains PII, such as an email address, appears in list rows as its stored value. Pseudonymization keeps that value. Use opaque identifiers if list access must stay PII-free.
The PUT response and the single-record GET return cleartext identity. The deanonymize permission gates only the single-record GET. Write access alone gates the PUT response, which echoes the full stored record, including any stored field the caller did not submit. Treat actor-mapping write access as PII-revealing. Audit logs can retain the raw actorId, which can be an email address.
List actor mappings
Cursor-paginated rows that omitdisplayName and email. Filter by an actor-ID prefix.
actorId (prefix filter), limit (default 25, capped at 100), and cursor.
Upsert an actor mapping
Creates or updates the PII for an actor ID.PUT is idempotent. The same call creates the record on first use and updates it thereafter. Supply at least one of displayName or email.
Get one actor mapping (de-anonymize)
Returns the cleartext PII for a single actor ID. This is the de-anonymization primitive, so the narrowerdeanonymize permission gates it rather than plain read.
Pseudonymize
Replaces the mapping’sdisplayName and email with [REDACTED] while preserving the record and its actorId link. This scrubs PII from the mapping only. Immutable audit records keep the raw actorId from the original write, and that value can itself be an email address. Historical audit logs and archived files keep their original values. Responds 204 No Content.
Delete a mapping
Permanently removes the mapping. Responds204 No Content.
Pseudonymize keeps the record and scrubs its PII. Delete removes the record entirely. Choose pseudonymize when you must retain the audit linkage, delete when the record itself must not persist.
Archives
The archival worker stays off by default (
ARCHIVAL_WORKER_ENABLED=false). When you enable it and configure archival storage, the worker compresses aging audit-log partitions and moves them to object storage. Matcher registers the archive retrieval routes whenever archival object storage is available, even when the worker stays off. The list endpoint returns completed archives. The download endpoint issues time-limited URLs for archive objects.
List archives
Offset-paginated. Filter by date range.from, to (YYYY-MM-DD or RFC 3339), limit (1–200, default 20), and offset. The endpoint lists only COMPLETE archives and never surfaces in-progress or failed archives.
Download an archive
Returns a presigned URL plus the checksum for integrity verification.Audit logs
Instrumented governance workflows write immutable, per-tenant audit records. Matcher links each record into a tamper-evident SHA-256 hash chain (
recordHash = SHA-256(prevHash || canonical content)), so inconsistent changes are detectable. Use the verify endpoint below for the server-side integrity verdict.
List audit logs
Cursor-paginated, with rich filters.actor, action, entity_type, date_from, date_to (YYYY-MM-DD or RFC 3339), limit (1–200, default 20), and cursor.
actor filters on the record’s actorId value. That value is the raw actor identifier from the original write, an email address in this example. Audit records store the identifier as-is. It does not have to match an actor-mapping actorId.
When a diff exceeds the outbox payload cap, changes carries a truncation-marker envelope instead of the full diff, and truncated becomes true with originalSize reporting the pre-truncation byte size.
Verify the audit chain
Re-verifies that every inspected record links to the previous one and matches its stored hash. Verification walks a contiguous span with amaxRecords bound, so intact speaks only for that inspected span. At the HTTP endpoint, a supplied maxRecords must be in the 1–10,000 range, and a value outside it returns 422. When you omit it, Matcher uses the 10,000-record default.
fromSeq is an optional verification floor (minimum 1). Omit it to start at sequence 1. A floor above 1 trusts that record’s stored prevHash, so the run does not cover tampering below the floor. The check stays strictly read-only. It detects tampering and never mutates a record.
intact field is true when the whole span stays unbroken. When the check finds a break, firstBrokenSeq reports the tenantSeq of the first failing record, and verifiedCount reports how many records held before it. The truncated field is true when the chain holds more records than the maxRecords inspection bound allowed. The response echoes the effective fromSeq. After a truncated run, resume with fromSeq = fromSeq + verifiedCount to inspect the next span.
Get one audit log
You can also list an entity’s history directly with
GET /v1/governance/entities/{entityType}/{entityId}/audit-logs (cursor-paginated), which is convenient when you already know the entity to audit.
