MemorySyncMemorySync
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

CategoryExamples
authLogin, logout, token refresh, API key usage, MFA challenge, SSO assertion.
securityAPI key created, rotated, revoked. Encryption key lifecycle. Permission grants.
dataMemory created, updated, deleted, purged. Bulk export. Bulk delete.
orgTenant created, plan changed, member added, role changed, project created.
apiRate 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 indexed

How the row is written — never blocking the parent operation

  1. 1The handler calls the audit logger on the same database session as the parent operation.
  2. 2Inside the safe-logging wrapper, every exception is caught — a logging failure cannot fail the parent request.
  3. 3The audit row commits in the same transaction as the business write. A successful POST /memory/add always 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

  1. 1Pull every audit_forwarders row that is enabled and not in OPEN circuit-breaker state.
  2. 2For each forwarder, read up to 100 audit rows beyond its last forwarded id (batch size cap).
  3. 3Render the SIEM-specific payload (Splunk HEC envelope, Datadog log envelope, or raw JSON for custom endpoints).
  4. 4POST to the endpoint with the configured headers, respecting the per-endpoint concurrency cap of 3.
  5. 5On success: advance the forwarder's last forwarded id, write a siem_delivery_logs row with delivery time.
  6. 6On failure: retry per the schedule (1 s → 2 s → 5 s → 10 s → 30 s + jitter, max 5).
  7. 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

StageTriggerEffect
ActiveDefault 90 days, configurable per tenant via audit_retention_policy.Visible to GET /org/compliance/audit; forwarded to SIEM.
ArchiveAfter active window expires.Hidden from the dashboard; queryable via the compliance export endpoint only.
Hard deleteWhen 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_forwarders row.