owpurge 0.2.0

Askar wallet pruning for multi-tenant SSI platforms: a single static binary that prunes stale exchange records straight from the wallet store
# owpurge — local dev credentials
# Copy to .env and fill in values. Never commit .env.
# In production, set these as environment variables directly rather than shipping a file.

# ── Wallet / store ───────────────────────────────────────────────────────────
# Match WALLET_ID and WALLET_KEY exactly from the agent controller's own .env.
# A wrong WALLET_KEY causes Askar to reject the store and the binary exits immediately.
WALLET_ID=your-wallet-id
WALLET_KEY=your-wallet-master-key
# NOT always 'raw'. Credo defaults to kdf:argon2i:mod when the agent never sets
# keyDerivationMethod, and Askar refuses to open a store with the wrong method. The
# At least one dedicated store on this platform is kdf:argon2i:mod, not raw. Check
# per wallet:
#   SELECT split_part(value, '?', 1) FROM config WHERE name='key';
# 'kdf:argon2i:13:mod' -> kdf:argon2i:mod | 'raw' -> raw
KEY_DERIVATION_METHOD=kdf:argon2i:mod

# ── RDS PostgreSQL ───────────────────────────────────────────────────────────
DB_HOST=your-rds-host.rds.amazonaws.com
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=your-db-password
DB_ADMIN_USER=postgres
DB_ADMIN_PASSWORD=your-db-password
# Askar's client-side Postgres pool size for THIS process. Default 50. Not especially
# sensitive: a delete batch is one transaction on one connection, so batch size does not
# drive pool pressure.
# DB_MAX_CONNECTIONS=50
# DB_MIN_CONNECTIONS=
# Askar's ?schema= parameter. RECOMMENDED: set it explicitly.
#
# Askar applies it as `search_path` on every pooled connection, so it pins WHERE the
# store is, independently of WHICH user connects.
#
# Credo never sets it, so a Credo-provisioned store sits in a schema named after the
# user the agent connects as (connecting as 'foo' puts the store in schema 'foo'), and
# is otherwise found only by connecting as that same user. That makes DB_USER silently
# load-bearing: get it wrong and Askar reports no store in a perfectly good database —
# which reads like a missing wallet, not a config error. Observed directly:
#
#   DB_USER=postgres, no DB_SCHEMA  ->  relation "config" does not exist
#   DB_USER=postgres, DB_SCHEMA set ->  store opens normally
#
# Setting it decouples the two: DB_USER becomes purely authentication. Note this
# diverges from how the agent itself opens the store (Credo omits the parameter), so
# the value has to be right — read it off each wallet rather than assuming:
#
#   SELECT table_schema FROM information_schema.tables
#   WHERE table_name IN ('config','profiles','items','items_tags')
#   GROUP BY table_schema HAVING count(*) >= 3;
#
# Leave unset only if you want to match Credo's own resolution exactly, in which case
# DB_USER must be the user that provisioned the store.
DB_SCHEMA=the-schema-holding-the-askar-tables

# ── Purge behaviour ──────────────────────────────────────────────────────────
# PURGE_MODE: "multi-tenant" (default) | "dedicated" (root wallet, no tenant profiles)
PURGE_MODE=multi-tenant

# Single tenant for the per-tenant commands (census, bulk-purge, orphan-sweep, ...).
TENANT_ID=the-tenant-id-to-purge

# Comma-separated tenant ids for `purge` to iterate. Overrides TENANT_ID.
# TENANT_ALLOWLIST=tenant-id-1,tenant-id-2

# Set to true to count eligible records without deleting. Always run dry first.
# Fail-safe: ONLY the literal "false" enables live deletes.
DRY_RUN=true

# Terminal records older than this many days are eligible (production default: 30).
# 0 = no age filter (all terminal records eligible).
TTL_DAYS=30

# Milliseconds to sleep between batches to limit RDS load. 0 disables.
THROTTLE_MS=250

# Progress line every N batches during long scans.
# HEARTBEAT_EVERY=20

# Batch sizes.
# BULK_PURGE_BATCH_SIZE=500
# ORPHAN_SCAN_BATCH_SIZE=2000
# ORPHAN_DELETE_BATCH_SIZE=500

# ── Stale-incomplete purge (bulk-purge only, opt-in) ─────────────────────────
# ALSO delete non-terminal (incomplete/abandoned) PROOF exchanges — e.g. proofs stuck
# in request-sent a holder never answered. Credential exchanges are intentionally
# excluded (see src/credo.rs). Off by default.
# PURGE_STALE_INCOMPLETE=true
# Age threshold for incomplete records, separate from and more conservative than
# TTL_DAYS. Must be > 0; anything invalid or <= 0 falls back to 90 as a fail-safe —
# except an exact 0 combined with STALE_ALLOW_ZERO_TTL=true.
# STALE_INCOMPLETE_TTL_DAYS=90
# Test-only escape hatch: allow a 0-day age floor (delete ALL incomplete proofs
# regardless of age). Only takes effect together with STALE_INCOMPLETE_TTL_DAYS=0.
# Never use in production.
# STALE_ALLOW_ZERO_TTL=true

# ── Audit trail ──────────────────────────────────────────────────────────────
# CSV appended to as each target finishes: when the purge ran, the parameters it ran
# with, and how many records of each category it removed. Parent directories are
# created automatically. Disable for a run with --no-audit.
# AUDIT_CSV=./logs/purge-audit.csv

# ── Safety gate ──────────────────────────────────────────────────────────────
# bulk-purge additionally requires this alongside DRY_RUN=false.
# BULK_CONFIRM=true