Storage security

Zero-config encryption at rest, ring retention, redaction, and what Mizpah does (and does not) protect.

Mizpah is built for local log browsing. By default logs live only in an in-memory ring. When something must hit disk, encryption and private file modes turn on automatically — no passphrases, no persistEncrypt flags, no key files to manage.

What lives where

LocationDefaultContents
In-memory ringAlways onQueryable log entries (plaintext in process for fast CEL/SQL)
Persist segmentsOff until persistDir is setEncrypted append-only segments under the config tree
Update spillTemporary, during self-updateEncrypted blob so the buffer survives a binary replace
Config dirAlwaysconfig.toml, PID files, optional sealed DEK fallback

Typical config dir paths (override with MIZPAH_CONFIG_DIR):

PlatformPath
macOS~/Library/Application Support/dev.ethira.mizpah/
Linux~/.config/mizpah/
Windows%APPDATA%\ethira\mizpah\

Enabling durable persist

Optional. Add to config.toml (camelCase):

persistDir = "persist"   # relative → under config dir; or an absolute path
maxBytes = 1073741824    # ring + disk prune budget (default 1 GiB)
ttlHours = 24            # age eviction; 0 disables (default 24)

CLI flags --max-bytes / --ttl-hours still apply to the hub ring. With persist enabled, disk segments are pruned to the same TTL / byte policy so sensitive rows do not linger forever on disk after they leave memory.

mzp hub restart clears the in-memory buffer; hydrated persist (if configured) reloads encrypted segments on the next hub start.

Encryption at rest (automatic)

Whenever Mizpah writes log payloads to disk (persist or update spill):

  1. A per-install data encryption key (DEK) is created on first use.
  2. The DEK is stored in the OS credential store when available:
    • macOS Keychain
    • Windows Credential Manager
    • Linux Secret Service / libsecret
  3. Each record (or spill blob) is sealed with AES-256-GCM (versioned framing + random nonce). Persist lines look like mzp1: + base64 — not readable JSON.
  4. On hydrate / spill restore, ciphertext is decrypted into the ring only.

macOS may show a one-time Keychain allow dialog the first time the hub needs the DEK. That is the only expected prompt; there is nothing to put in config.toml for encryption.

Keychain unavailable (silent fallback)

Headless Linux without Secret Service, locked-down environments, or CI:

Same-user backups of the entire config directory remain a residual risk in fallback mode; prefer the OS keychain when possible.

Legacy plaintext segments

Older installs may have plaintext segment-*.ndjson files. On hydrate, Mizpah loads them, rewrites encrypted segments, and drops the plaintext. No manual migration step.

Filesystem hardening

Applied automatically when writing persist / spill / key material:

In-memory protections

The working ring stays plaintext in process so search stays fast. Mizpah still reduces accidental leakage:

ControlBehavior
TTL + maxBytesDefault 24h / 1 GiB; shorter dwell = smaller blast radius
Core dumpsDisabled for the hub (RLIMIT_CORE=0)
Linux ptrace barHub sets non-dumpable (PR_SET_DUMPABLE=0) — blocks casual same-user debugger attach (not root)
Best-effort redactionCommon patterns redacted at ingest (Authorization, Bearer …, api_key=, PEM blocks, etc.). Not a DLP product
SQL snapshotEphemeral in-memory SQLite only — not written to disk

Threat model

In scope (what storage security targets)

Out of scope

For shared or remote hubs, combine loopback / SSH tunnels with optional Custom auth (OIDC).