feat(config): encrypted local-settings store replacing .msh (VA-33) #30
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/encrypted-settings-store-VA-33"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements the VA-29 design (
docs/local-settings-store.md). Closes VA-33.Replaces the plaintext
.mshconfig and its text parser with an at-rest encrypted local-settings store.What changed
ring, already in the dep graph). Layout ismagic || version || kdf_id || aead_id || reserved || nonce(12) || ciphertext || tag(16), with the 20-byte header authenticated as AEAD associated data so an algorithm/version downgrade is caught.base32(SHA-384(install_secret)[..20]).vss, a one-way commitment that leaks neither the secret nor the key. The secret lives in a separate0600install.secretsidecar (the confirmed default), so bytes copied off-host without it stay undecryptable.zeroize-wrapped.MshConfig->SettingsMap. The.mshtext reader survives only behind themigratefeature for the one-shot migration.ServerSettings/ConnectionSettingsand their tests are unchanged.--msh <file>->--settings <dir>, plus--secret-file. Newconfigsubcommand:show [--reveal],get,set,seal..vsswins; else a siblingvervain-agent.mshis migrated and rotated to.msh.bak(rename, never delete); else a clear error tells the operator to seal one.installseals its--fromseed into the store and provisions the sidecar; the systemd unit runs with--settings; uninstall removes the.vss+ sidecar.ensure-msh{,-host}->ensure-settings{,-host}(render the plaintext seed, clear any stale sealed store so a re-render takes effect); dev recipes and the app container run with--settings.Acceptance criteria
.vssround-trips; all required + optional keys preserved (sealed_round_trip_preserves_all_keys).ServerSettings/ConnectionSettingstests pass unchanged..msh->.vssmigration with.msh.bakrotation, tested (migrates_legacy_msh_and_rotates_to_bak).config show / get / set(andseal) subcommands land.--settings.data-encoding, already in the lock).Notes
.msh.templatefilename is kept (not renamed tosettings.templateas the design sketched) becausevervain-agent.mshis the load-bearing name the migration path looks for. The rendered seed is still plaintext dev input, not a secret.Verification
cargo clippy --workspace --all-targets -- --deny warningsandcargo fmt --all --checkare clean.cargo test --workspacepasses exceptagent_completes_secondary_handshake, which fails identically on cleanmainin the dev container (it assertsPlatformType::Desktopbut virtualization detection reportsVirtualinside Docker). Pre-existing and unrelated to this change.Implements docs/local-settings-store.md. Replaces the plaintext .msh config and the line-oriented text parser with an at-rest encrypted store: the settings map is serialized to a length-prefixed blob and sealed with ChaCha20-Poly1305 under a key derived (HKDF-SHA-384) from a 256-bit per-install secret. The store filename is a SHA-384 commitment to that secret (base32, `<stem>.vss`), and the secret lives in a separate 0600 `install.secret` sidecar so the bytes stay undecryptable when copied off-host without it. The 20-byte header (magic, version, algorithm ids, nonce) is authenticated as AEAD associated data, so an algorithm/version downgrade is detected. Decryption is fail-closed on a bad tag, truncation, bad magic, or unsupported algorithm. vervain-agent-config: `MshConfig` becomes `SettingsMap`; the text parser (`FromStr`, `parse_msh_text`) is retained only behind the `migrate` feature for the one-shot migration; adds `serialize`/`deserialize`, `open_file`/`seal_to_file`, and the `store` module (ring HKDF + ChaCha20-Poly1305 + SystemRandom, zeroize-wrapped key material). `ServerSettings` / `ConnectionSettings` and their tests are unchanged. vervain-agent: new `settings` module resolves the store directory, loads (existing `.vss` wins, else migrates a sibling `vervain-agent.msh` and rotates it to `.msh.bak`, else errors), and seals from a plaintext seed. CLI: `--msh <file>` becomes `--settings <dir>` plus `--secret-file`; adds a `config` subcommand (`show [--reveal]`, `get`, `set`, `seal`). `install` seals its seed (`--from`) into the store and provisions the sidecar; the systemd unit runs with `--settings`; uninstall removes the `.vss` + sidecar. justfile/compose: `ensure-msh{,-host}` become `ensure-settings{,-host}`, which render the plaintext seed and clear any stale sealed store so a re-render takes effect; the agent migrates the seed to a `.vss` on startup. Dev recipes and the app container run with `--settings`. The `.msh.template` filename is kept (not renamed to `settings.template` as the design sketched) because `vervain-agent.msh` is the load-bearing name the migration path looks for; the rendered seed is still plaintext dev input, not a secret. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>