How It Works
Audit & SIEM Forwarding
Every sensitive operation in MemorySync writes one audit row, and every audit row can be forwarded to your own SIEM (Splunk HEC, Datadog, custom HTTPS endpoint). This page describes what gets audited, how forwarding is delivered reliably, and how retention is enforced.
What gets audited — the five categories
| Category | Examples |
|---|---|
| auth | Login, logout, token refresh, API key usage, MFA challenge, SSO assertion. |
| security | API key created, rotated, revoked. Encryption key lifecycle. Permission grants. |
| data | Memory created, updated, deleted, purged. Bulk export. Bulk delete. |
| org | Tenant created, plan changed, member added, role changed, project created. |
| api | Rate limit hit, suspicious request pattern, blocked request. |
The audit row, exactly as the database stores it
TEXT
audit_logs
──────────
id bigint pk
tenant_id string indexed
user_id int nullable indexed
event_type string e.g. "memory.create", "auth.login"
resource_type string e.g. "memory", "api_key"
action string e.g. "create", "rotate"
status string "success" | "failure"
ip_address string client ip
user_agent string truncated
payload_json json non-PII context
created_at timestamp indexedHow the row is written — never blocking the parent operation
- 1The handler calls the audit logger on the same database session as the parent operation.
- 2Inside the safe-logging wrapper, every exception is caught — a logging failure cannot fail the parent request.
- 3The audit row commits in the same transaction as the business write. A successful
POST /memory/addalways produces both a memory row and an audit row, atomically.
The after-commit hook that fans out to SIEM
An after-commit hook on the database session fires once the transaction lands. It enqueues the audit row's id for forwarding — never the row body, so the forwarding step always reads the canonical version, never a stale snapshot. If no SIEM forwarder is configured for the tenant, the enqueue is a no-op.
The forwarder tick, every 5 minutes
- 1Pull every
audit_forwardersrow that is enabled and not inOPENcircuit-breaker state. - 2For each forwarder, read up to 100 audit rows beyond its last forwarded id (batch size cap).
- 3Render the SIEM-specific payload (Splunk HEC envelope, Datadog log envelope, or raw JSON for custom endpoints).
- 4
POSTto the endpoint with the configured headers, respecting the per-endpoint concurrency cap of 3. - 5On success: advance the forwarder's last forwarded id, write a
siem_delivery_logsrow with delivery time. - 6On failure: retry per the schedule (1 s → 2 s → 5 s → 10 s → 30 s + jitter, max 5).
- 7On consecutive-failure threshold (5 in a row): trip the circuit breaker for the cooldown window, dead-letter the in-flight batch.
What the customer sees in their SIEM
- Every payload carries the audit row id, so deduplication on the SIEM side is trivial.
- Every payload carries the tenant id, so multi-tenant SIEMs can route by tenant without parsing the body.
- Every payload carries the source request id, so the SIEM event can be joined back to a request log on the API side.
- Failed deliveries that reach dead-letter are kept in object storage with their original envelope, so an operator can replay manually after the endpoint is repaired.
Retention — how long audit rows live
| Stage | Trigger | Effect |
|---|---|---|
| Active | Default 90 days, configurable per tenant via audit_retention_policy. | Visible to GET /org/compliance/audit; forwarded to SIEM. |
| Archive | After active window expires. | Hidden from the dashboard; queryable via the compliance export endpoint only. |
| Hard delete | When auto_purge_enabled=true and the retention window passes. | Row removed permanently. Cannot be recovered. |
What this flow is not
- Not a real-time stream. Forwarding is batch-driven on a 5-minute cadence by default. If your SIEM contract requires sub-minute delivery, configure the cadence accordingly — but be aware of the circuit-breaker semantics.
- Not a debug log. Audit rows are designed for compliance and security investigations. Application-level debugging belongs in the structured request log, not here.
- Not unauthenticated. Every audit forwarder is authenticated to the SIEM with the tenant's configured credential — keys are stored encrypted in the
audit_forwardersrow.