Implement read-only vault domains (#6)

This commit is contained in:
Hermes Agent
2026-08-09 22:22:21 +00:00
parent 34ce731716
commit dca07b7fbd
9 changed files with 1039 additions and 0 deletions

43
docs/read-domains.md Normal file
View File

@@ -0,0 +1,43 @@
# Read-only password-store domains
`VaultReader` in `crates/storage` owns the semantics for `list`/`ls`, explicit
and implicit `show`, `find`/`search`, and decrypted `grep`. Frontends receive
typed models and render storage-provided paths; they do not inspect tree text,
ciphertext names, or decrypted display strings to infer domain state.
## Trees and dispatch
Tree models contain separate entry and directory node kinds, logical paths,
names, and deterministic children. Entry names never contain the `.gpg` storage
suffix. Policy files, signatures, other metadata, `.git`, and `.extensions`
trees are not nodes. The plain renderer produces the stable uncolored tree used
by command-line output.
No show path selects the root tree. A path is resolved against the repository
snapshot: an entry decrypts to redacted `SecretBytes`, while a directory returns
its subtree. Missing and ambiguous paths remain typed repository errors and map
to the normal `pass` failure status.
Clipboard and QR requests use the parsed `Presentation` enum and a nonzero line
number. Storage selects that line directly from decrypted bytes and returns a
`PresentationSecret` containing the logical entry, line, channel, and redacted
zeroizing contents. The adapter never parses rendered terminal output, and a
missing or empty requested line is an explicit failure.
## Name and plaintext search
Find performs Unicode case-insensitive substring matching against logical entry
and directory names. Multiple terms are alternatives, as in upstream `pass`.
Results include typed matches plus a pruned deterministic tree with the
necessary ancestors. Hidden implementation directories, metadata, and
ciphertext suffixes cannot become matches.
Decrypted grep uses the Rust `regex` byte engine, which provides linear-time
matching without invoking GNU grep. The accepted command contract is limited to
case-insensitive, inverted, line-number, and fixed-string behavior; unsupported
GNU options fail during command parsing. Entries are visited in repository
order, each plaintext is decrypted only while its lines are examined, and the
full plaintext buffer is zeroed when that iteration ends. Matched lines are
copied into redacted `SecretBytes`; rendered grep output is also returned as
`SecretBytes`, and both are zeroed with their result values. Debug output
includes paths and counts but never matched contents.