KV persistence and access control

When KV changes are written to maand.db, who can read/write which namespaces, and how purge/GC works.

Key reference and examples: namespaces.md ยท Index: README.md

Inspect:

maand cat kv
maand cat kv --jobs api --active
maand cat kv get --reveal secrets/job/api db_password

Namespace overview

Namespace Written by Survives rebuild? Notes
maand/bucket build yes (synced) Global: bucket_id, jobs, active_jobs, port names
maand/worker/<ip> build yes Worker metadata, labels, peers
maand/worker/<ip>/tags/<key> build yes From workers.json tags
maand/job/<job> build yes (when job active) Job metadata, version, rollout_order, workers
maand/job/<job>/worker/<ip> build + deploy yes (when alloc active) Certs, peers, version
vars/bucket build yes From bucket.conf
vars/bucket/job/<job> build yes (when job active) From bucket.jobs*.conf
vars/job/<job> build + hooks yes App config; not wiped on rebuild
secrets/job/<job> hooks yes AES-256-GCM encrypted
maand/prometheus build yes (synced) Scrape configs only โ€” alerts/runbooks/dashboards stay in job_files โ€” prometheus

When a job is removed from the workspace, maand build tombstones vars/job/<job> and secrets/job/<job> automatically. While the job remains in the workspace, both namespaces are retained unless you pass --purge-secrets-kv and the job has no active allocations (removed = 0 and disabled = 0). Build-owned namespaces for jobs whose allocations are all removed are still purged each build; maand gc purges remainder. See cli/gc.md.


Who can read which namespaces

Hooks and templates on allocation (job, worker_ip) may read:

maand cat kv --jobs <job> lists the same union across all non-removed allocations.

Writes from hooks are limited to vars/job/<current job> and secrets/job/<current job>. Full matrix: hook-api.md.


Persistence timing

Context When writes hit maand.db
maand build End of main transaction; post_build hooks persist in a follow-up transaction
maand deploy After each job's pre_deploy and after each deployJob (KV checkpoint)
maand hooks On successful CLI exit
maand health_check Read-only (mutations rejected)

Deploy checkpoints roll back if the deploy transaction aborts before commit. Partial deploy commits KV for successful jobs.


Version history

KV keeps multiple versions per key. maand gc --retain-days N trims deleted history. Latest active keys: maand cat kv --active.


Key TTL

Hooks may set an optional ttl (seconds) on PUT /kv and /kv/secret. 0 or omitted means the key does not expire.

Phase Behavior
Write Stores ttl and created_date (Unix seconds) on the new version row
maand build Tombstones keys where created_date + ttl <= now (skipped for keys changed in the same build)
maand gc Purges tombstoned rows and old version history per --retain-days (unchanged)

Build-synced namespaces (maand/*, vars/bucket/*, workspace vars.conf keys) always write ttl = 0. TTL applies mainly to runtime-written vars/job/<job> and secrets/job/<job> keys.

Example (Python):

maand.put_job_variable("session_token", token, ttl=3600)  # expire after 1 hour; tombstoned on next build after expiry

Between builds, an expired key remains readable until maand build runs. After tombstone, Get and hook reads treat the key as missing.