Separation of duties
Four governance decisions refuse the person who asked for them, whatever role that person holds:
The refusal is not advisory. For grants and remediation it is enforced in the domain, in the repository’s conditional update, and by a database constraint; the attempt is returned as
403 and written to the audit trail as a failure.
One approval is enough for a grant or an exception, and that threshold is set for the deployment rather than per organization or per risk level.
The audit trail
GET /api/v1/audit/records returns the trail, newest first, filterable by action, actor, result, subject and time window. It needs audit:read.
Inside TRUSTIVAN,
audit:read belongs to the Organization Admin role only. A Security Analyst and a Viewer can read the inventory, the findings and the runtime decision log, and cannot read the audit trail.authz.denied with the reason and the roles the caller held, and a refused self-approval is written as a failure of the action it attempted. A trail of only successful actions would be the least interesting half.
What is recorded
Roughly 120 action names across sign-in and sessions, people and teams, roles, organization settings, connectors and synchronizations, identity ownership and quarantine, credential state and rotation, policy configuration and evaluation, finding triage, activity and exposure collection, resource classification, agents and their tools, remediation, access grants, runtime rules, runtime credentials, policy exceptions, access certification, authority delegation and evidence export. Some names are reserved in the model and nothing produces them yet — discovery-time events such asidentity.discovered and credential.discovered among them. The product enumerates which, with the reason, rather than leaving a reader to guess why a category is empty.
Three status changes on a finding are audited — accepting risk, recording a false positive, and reopening. Acknowledging a finding and starting work on it are not.
Secrets never reach it
Every audit write passes through a redaction step that replaces values whose key names look like a password, a secret, a token, a credential, an authorization or cookie header, an API key, a private or encryption key, or a session id. It recurses into nested detail. If the audit write fails, the action that caused it is rolled back with it. There is no path where something happens and the record of it quietly does not.The hash chain
Each audit record stores a digest of itself and the digest of the record before it, per organization. The digest covers the record’s identity, actor, action, subject, result, timestamp, correlation id and detail, together with the previous digest — so altering any field, removing a record, or inserting one out of order breaks the chain from that point onward. The chain is written by a database trigger, not by application code, under a per-organization lock, so concurrent writes cannot fork it and no code path can skip it. A database rule permits exactly one update to an audit row — stamping the digests — and refuses every other change.Verifying it
GET /api/v1/audit/integrity walks the chain and returns whether it is intact, how many records were checked, and where the first break is, with one of two reasons:
- a record was removed, reordered, or inserted — the link to the preceding record does not match;
- a field was altered — the record’s own digest does not match its content.
200 with isIntact: false, not an error. It is an answer, and one you want to be able to read. Verification writes no audit record of its own.
Evidence export
GET /api/v1/audit/evidence returns a window of the trail as a JSON package built for somebody else to check. It needs audit:read. Up to 1,000 records by default and 5,000 at most; a window that does not fit is reported as truncated with the id to resume from, never silently trimmed.
The manifest carries the format version, the organization, who exported it and when, the window, the record count, the first and last record ids, the chain anchor, a digest over the exported records’ chain values, whether the chain verified at the moment of export, and written instructions for verifying the package independently.
The export is itself audited as audit.evidence.exported, so the next export covers it: who took a copy of the trail is part of the trail.
Retention
There is no retention job and no retention setting. Audit records are kept, and deletion is refused by the database unless a transaction explicitly opts into a retention operation — which nothing in the product does today. Plan capacity on that basis.Access certification and delegation
Two further governance surfaces exist:- Access certification — campaigns in which named reviewers confirm or reject a population of identities, ownerships or entitlements. The person who opens a campaign may not answer it.
- Authority delegation — a record that one person is acting with another’s authority for a period, requested by one person and approved by another.
certification:read and delegation:read, which every role holds.
Nothing closes an expired campaign, delegation or policy exception in the background. Each stops being in force on its end date — that is evaluated whenever it is read — but its stored state stays as it was, and the
expired audit record for it is never written. Treat the end date, not the state, as the authority on whether something is still in force.
