Skip to content

Backups

Everything that’s not a secret:

  • Tasks + events (volume depends on traffic).
  • Agents (names, token hashes, capabilities).
  • Users, memberships, sessions (sessions are ephemeral).
  • Audit log (unlimited retention by default - most important to back up).
  • Schedules.
  • Z4J_SECRET, Z4J_SESSION_SECRET. (Z4J_SECRET doubles as the audit-log HMAC key; there is no separate audit secret.)
  • Agent plaintext tokens (only the operator who minted them holds these - brain stores hashes).
  1. Postgres - pg_dump or managed-service snapshot, at least daily. Point-in-time recovery for RPO < 1 hour.
  2. Secrets - version-controlled in your secret manager (Vault, AWS Secrets Manager, GCP Secret Manager).
  3. Agent tokens - re-mintable. If lost, revoke the old agent and mint a new token. No need to back up plaintext.
  1. Restore Postgres from backup.
  2. Point a new brain container at the restored DB.
  3. Verify the audit chain: z4j audit verify.
  4. Agents auto-reconnect (their tokens are still valid - token hashes are in the restored DB).
  5. Spot-check: log in, list tasks, check agent status.

Run this drill at least quarterly in staging.

For compliance, export the audit log separately:

Terminal window
curl -H "Authorization: Bearer $TOKEN" \
"https://z4j.example.com/api/v1/audit/export?format=csv&from=2026-01-01" \
> audit-2026-q1.csv

CSV includes row_hmac and prev_row_hmac - external systems can verify the chain independently.

  • Events - tune Z4J_EVENT_RETENTION_DAYS (default 30). Older partitions are dropped by the retention worker.
  • Audit log - tune Z4J_AUDIT_RETENTION_DAYS (default 90). Older rows are pruned by the retention worker; the sweeper records a summary row per batch so chain continuity is preserved across the prune.

If you rotate Z4J_SECRET, the audit chain from that point onward uses the new secret. Pre-rotation rows are still verifiable: list the prior secret in Z4J_PREVIOUS_SECRETS (comma-separated) so the verifier accepts older HMACs while writes use the new key. If you restore Postgres from before rotation, the matching Z4J_PREVIOUS_SECRETS entry must still be present.