# Svault 0.6.0 — Findings Carry-Forward

Carry-forward of the [0.5.0 register](0.5.0.md), as that document instructs: each
0.5.0 finding is restated here as **Fixed**, **Improved**, or **Unchanged** for
0.6.0. The 0.5.0 register remains the document of record for 0.5.0; this is the
delta. Numbering matches 0.5.0.

- **Version:** 0.6.0
- **Date:** 2026-05-29
- **Maintainer:** Nima Karimi
- **Tooling pass:** `cargo test` (82 pass, 1 ignored benchmark), `cargo clippy --all-targets` (clean), `cargo build --release` (clean)
- **New artifact:** [stress/0.6.0.md](../stress/0.6.0.md) — logged concurrency / pressure simulation

> Dispositions and status below are the maintainer's decisions; the AI reviews
> from 0.5.0 informed them but did not make them.

## What changed in 0.6.0

Four findings were acted on this release — the three "to fix first" items from
the 0.5.0 carry-forward plus the connection-DoS work, with a logged stress
simulation backing the last one.

| # | Finding (short) | 0.5.0 status | 0.6.0 status | What changed |
|---|-----------------|--------------|--------------|--------------|
| 4 | Session writes raw passphrase to disk (incl. TUI, Windows) | To fix | **Fixed (key-at-rest); Windows ACL still open** | `.session` now caches the 32-byte **derived key** (hex, mode 0600 on Unix), never the passphrase — on every platform incl. the TUI. A stolen `.session` opens that one vault but no longer leaks the reusable passphrase. Daemon (in-memory) stays preferred. New test asserts the session never contains a passphrase. *Remaining:* Windows still writes the key without an ACL/DPAPI equivalent; "deprecate `.session` / route the TUI through the daemon" is still future. |
| 8 | No connection limit / rate-limit on the daemon (DoS) | Planned | **Fixed** | `daemon.max_connections` (config, default 512) caps live handler threads; 30 s per-connection read timeout stops a stalled client pinning a slot; `ConnGuard` frees slots even on panic. A logged [stress simulation](../stress/0.6.0.md) (128k concurrent reads) validates: 0 wrong values, default ceiling sheds nothing under 64-way load, the cap sheds cleanly when tuned tight. |
| 13 | Mutex poisoning kills the daemon (`lock().unwrap()`) | To fix | **Fixed** | The key-store lock is taken with poison recovery (`unwrap_or_else(into_inner)`), so a panicking handler can no longer abort the whole daemon. New test poisons the mutex and asserts the store stays usable. |
| 20 | `unwrap()` on a length slice panics on a truncated `vault.enc` | To fix | **Fixed** | `save_secrets` length-checks the salt slice and returns an error. New test truncates `vault.enc` below `SALT_SIZE` and asserts an error, not a panic. |

Also added (not a 0.5.0 finding, surfaced by the stress run): **connect
resilience** — `daemon::send` retries the socket connect with short backoff, so
an OS listener-backlog drop under connect-churn is served rather than failing
hard. This removed the ~0.01% hard-error tail seen in pre-retry runs.

## Unchanged (status as recorded in 0.5.0)

These keep their 0.5.0 disposition; nothing changed this release. See
[0.5.0.md](0.5.0.md) for the full justification.

| # | Finding (short) | Status carried forward |
|---|-----------------|------------------------|
| 1 | No socket peer-credential check (same-UID trust) | Approved — documented threat model; `SO_PEERCRED`/`getpeereid` bond still the planned direction |
| 2 | Policy engine is advisory, not enforced in the daemon | Planned |
| 3 | Passphrase sent in cleartext over the socket on `Unlock` | Planned — client-side key derivation |
| 5 | Policy file unsigned + unbounded upward search | Planned |
| 6 | Decrypted values / `Unlock` passphrase as un-zeroized `String` | To check |
| 7 | Static salt reuse on vault writes | OK |
| 9 | No `cargo audit` / `cargo-deny` gate in CI | Planned |
| 10 | TUI-only transitive advisories (`lru`, `paste` via `ratatui`) | Planned — `ratatui` bump |
| 11 | No release-artifact signing / SBOM / checksums | To check |
| 12 | Passphrase strength advisory, no entropy floor | To check |
| 14 | `recovery.enc` / export bundles world-readable; export = key-equivalent | Planned |
| 15 | Audit/usage logs attacker-mutable, not tamper-evident | OK (needs improvement) |
| 16 | Socket-perm TOCTOU + world-traversable `.svault/` | Planned (incl. machine-ID bind idea) |
| 17 | Daemon hygiene: no `SIGTERM` zeroize, unrotated log, start race, coarse ticker | Planned |
| 18 | Recovery code has no checksum/parity digit | OK |
| 19 | `Vault::open` decrypts the whole DB just to verify the passphrase | OK |
| 21 | GCM uses no AAD (salt not authenticated) | OK |
| 22 | Fallback policy reads `meta.yaml` unverified on the `get` path | Planned |

## Working order for 0.7.0

1. **High-consensus planned** — #9/#10 (CI `cargo audit` gate + `ratatui` bump),
   #2/#5 (policy enforcement + signed/pinned policy), #3 (client-side key
   derivation so the passphrase never crosses the socket).
2. **Finish #4** — Windows DPAPI/ACL or refuse-to-cache, and route the TUI
   through the daemon so `.session` can be deprecated on Unix.
3. **To check / decide** — #6 (zeroize secret/passphrase strings), #11
   (release-artifact integrity), #12 (passphrase entropy floor).
4. **Remaining planned** — #1 peer-UID bond, #14, #16, #17, #22.
