How Versus records who did what — a searchable activity log across every workspace, plus tamper-evident, hash-chained audit trails in the sensitive subsystems (Vault, Versus Desk change management, privileged sessions) whose integrity you can verify on demand.
Versus records who did what, to which resource, and when across the platform. There are two complementary layers, used for two different jobs:
The activity log answers what happened. The hash chains additionally prove the record hasn't been altered since.
The activity log is a single append-only table of Audit records. Anything security-relevant an operator or the platform does — creating and updating inventory, toggling rules, running upgrades, taking backups, signing in, vault operations — lands here as one row.
Each record carries:
| Field | Meaning |
|---|---|
user | The actor — a username, or system for platform-initiated actions. |
action | One of create, update, delete, toggle, upgrade, backup, login, other. |
resource_type | The kind of object touched, e.g. vault_secret, a device, a rule. |
resource_id | The object's identifier, where one applies. |
resource_name | A human-readable label for the object. |
details | A JSON blob of action-specific context. |
ip_address | The source address, where available. |
timestamp | When it happened (set at write time, never editable). |
The log is exposed read-only — there is no API to create, edit, or delete an audit row by hand. Fetch it with a standard authenticated request (see Running Workflows via the API for how to obtain a bearer token):
GET /api/audit-log/
Authorization: Bearer eyJhbGc…
Results are ordered newest-first and can be narrowed with query filters and full-text search:
GET /api/audit-log/?action=delete&resource_type=vault_secret
GET /api/audit-log/?user=svc-automation
GET /api/audit-log/?search=bigip-pair-1
| Filter | Matches on |
|---|---|
action | Exact action type. |
resource_type | Exact resource kind. |
user | Exact actor. |
search | Substring of resource_name or user. |
Because the endpoint is tenant-scoped, a caller only ever sees audit rows for their own tenant — the same row-level isolation that guards the rest of the platform (see Platform Architecture) applies to the audit log itself.
For the subsystems that hold secrets, gate change, or broker privileged access, a flat log isn't enough — you also need to prove the record wasn't quietly edited after the fact. Versus does this with hash-chained audit events.
The design is the same everywhere it appears:
seq and stores the hash of the previous event as prev_hash.hash is a SHA-256 digest computed over its contents and that prev_hash, so every row is cryptographically bound to the one before it.The consequence: altering or removing any historical event changes its hash, which breaks the link every later event depends on. A verification pass walks the chain in order, recomputes each hash, and reports the first sequence number where the stored value and the recomputed value disagree — pinpointing exactly where the tampering occurred.
Four subsystems maintain chains, each scoped to its own domain rather than a single global ledger.
Every operation against the Secret Vault writes to the chain — reads included. The recorded actions cover the full lifecycle of a secret:
| Action | When it fires |
|---|---|
write, read, delete, destroy | Secret created, accessed, soft-deleted, hard-destroyed. |
lease, renew, revoke | Dynamic/leased credential granted, extended, revoked. |
rotate, rotation_failed | Secret rotation succeeded or failed. |
checkout, checkin, force_release | Exclusive credential check-out lifecycle. |
reveal_request, reveal_approved, reveal_denied, reveal_consumed | The break-glass reveal-approval flow. |
enroll, unenroll | A device credential brought under (or removed from) vault management. |
A vault operation writes to both layers at once: it appends a flat Audit row (so it shows up in the activity log and reports alongside everything else) and a hash-chained VaultAuditEvent (so its integrity is independently provable). Each chained event records the actor, the action, the mount and path acted on, and an action-specific details payload — never the secret material itself. This is the trail behind the "day in the life of a secret" story: from enrollment through every read and rotation to destruction.
Versus Desk keeps a hash-chained trail of the events that move a ticket through its lifecycle — the record an auditor asks for when reviewing a change. Event types include state transition, assignment, comment, approval_decision, SLA sla_warn / sla_breach, knowledge-base kb_publish, and external sync. Each event's hash is computed as:
hash = SHA-256( prev_hash + canonical_json(payload) )
where canonical_json is a stable, key-sorted serialization, so the same payload always hashes the same way. This gives you a defensible chain of custody for approvals and change execution that ties into the workflow engine's own durable run records.
Every privileged session the platform brokers — SSH or HTTPS, where the credential is injected by Versus and never shown to the operator — carries its own per-session hash chain. Events capture the session lifecycle: connect, inject, command, proxy, approve, terminate, close, and rotate (the rotate-on-close that swaps the credential once the session ends). Because the chain is scoped to a single session, you can hand over a self-contained, verifiable record of exactly what happened during one privileged access episode.
The firewall-automation subsystem records an audit entry for each access-request change with a before_hash and after_hash — digests of the rule state on either side of the change. This is a point-in-time integrity check on what was changed (proving the before/after state), distinct from the append-only chains above that prove the sequence of events is intact.
Each chain exposes a verification routine that walks it in sequence order, recomputes every hash, and reports whether the chain is intact.
For Versus Desk, verification is a first-class API action, gated behind the itsm.manage permission:
GET /api/itsm/audit/verify/
Authorization: Bearer eyJhbGc…
200 OK
{ "ok": true, "message": "chain verified" }
If a row had been altered or removed, ok is false and message names the exact break, e.g. hash mismatch at seq=418: recomputed … != stored ….
For privileged sessions, the verification result is returned inline whenever you read a session's events, so the integrity check travels with the evidence:
GET /api/psm/sessions/{id}/events/
200 OK
{
"events": [ … ],
"chain": { "events": 12, "intact": true, "broken_at": null }
}
The vault chain's verification routine returns the same shape — { "events": N, "intact": true|false, "broken_at": null|<seq> } — walking every VaultAuditEvent from the first sequence number and stopping at the first row whose stored hash or back-link doesn't match.
/api/audit-log/ by resource_type, user, or a search term to find every action against a device, rule, or secret, then read the details payload for the specifics.intact: true result together with the events.Audit is one of the platform's secure-by-default guarantees, not an add-on you enable. It sits across the layers described in Platform Architecture:
The activity log gives you fast, searchable answers; the hash chains give you defensible, verifiable proof. Together they let you reconstruct — and stand behind — the history of change across your network estate.