Audit trail
What is recorded
Section titled “What is recorded”Every change of access and every instruction to a scanner:
- who was given, changed or lost access to what — grants, links, folders restricted, members and teams;
- who changed the organization’s policy, vocabulary, plan, domains, SSO and SCIM;
- who told an agent to sweep, who changed what it sweeps, paused or deleted it, and what happened to API keys.
Each entry names who did it, what it concerned, and when. Entries are append-only. Reads are not recorded.
| Who reads it | What |
|---|---|
| Every member | The agents’ activity: agents and keys |
| Admins | The whole trail |
The whole trail is for admins because it says who was let into restricted folders.
Export
Section titled “Export”Admins export the trail as CSV or JSON for any window of up to a year (30 days by default), ten times a minute at most. Each export is itself recorded before the first byte is sent. In CSV, cells that a spreadsheet would read as a formula are prefixed with '.
Retention
Section titled “Retention”Set in the security policy: 30 to 3650 days, or keep everything. Older entries are deleted daily.
Streaming
Section titled “Streaming”Send every new entry, as it happens, to your own system. Admins add up to five streams; creating, changing, rotating and removing one asks for a second factor.
Delivery is at least once: events at least five seconds old are sent in order, up to 100 per batch, and a stream moves on only when the receiver accepted. Deduplicate on the event id. Failures back off, doubling up to six hours; after 30 consecutive failures the stream is turned off and that is recorded. Send test event sends one made-up event.
Webhook
Section titled “Webhook”An HTTPS endpoint receiving POST requests:
{ "deliveryId": "…", "events": [{ "id": "…", "action": "access.expire", "…": "…" }] }Each request is signed with the stream’s secret, shown once at creation:
X-Infrality-Signature: t=1761300000,v1=5f2b…v1 is the hex HMAC-SHA256 of t, a full stop, and the raw body. Verify it before trusting the body, and refuse a t more than five minutes from your clock:
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(secret, header, rawBody, now = Date.now() / 1000) { const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('='))); if (Math.abs(now - Number(t)) > 300) return false; const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest(); const given = Buffer.from(v1, 'hex'); return given.length === expected.length && timingSafeEqual(given, expected);}Syslog
Section titled “Syslog”RFC 5424 messages, facility 13, octet-counted over TLS, to a host and port you give. The host must have a public address.