Skip to content

feat(cli): command-line scaffold - #27

Merged
ericliswan merged 4 commits into
mainfrom
feat/cli-scaffold
Oct 27, 2026
Merged

ericliswan merged 4 commits into
mainfrom
feat/cli-scaffold

Conversation

@ericliswan

Copy link
Copy Markdown
Owner

Summary

Adds the pwv command-line skeleton: clap definitions for every v1.0 command and the global --vault / --password-stdin flags, the mapping from errors to exit codes (3 for vault integrity or authentication failures, 1 otherwise, 2 from clap for usage errors), and a prompt module that reads secrets with a hidden terminal prompt or, with --password-stdin, one line each from stdin. Commands still report "not implemented"; each one arrives in its own PR with its flags.

Closes #26

Type of change

  • feat - [ ] fix - [ ] docs - [x] test - [ ] refactor - [ ] ci/chore - [ ] security

How was this tested?

  • cargo fmt --all --check, cargo clippy --workspace --all-targets --all-features -- -D warnings, cargo test --workspace --all-features, cargo deny check
  • Unit tests: exit-code mapping (including errors wrapped in context), stdin prompt reading (CRLF/LF, missing final newline, EOF)
  • tests/cli.rs (assert_cmd): help lists every command, help marks --password-stdin as automation-only, --version, usage errors exit 2

Security checklist

  • No secret in args, env vars, logs, Debug output, errors or panics (secrets are prompted only; PWV_VAULT holds the path only)
  • Crypto is called only from pwv-core::kdf / pwv-core::crypto
  • Any new header/file-format field is inside the AAD and validated on load (no format change)
  • Failure paths have tests (wrong password, tampering, bad input)
  • Docs (README / DESIGN / THREAT_MODEL) updated if behaviour changed (DESIGN.md exit-code policy)

Notes for the reviewer

  • Every pwv-core error except Io exits 3, so a malformed or downgraded header counts as an integrity failure, not a user error.
  • Stdin answers are read into a preallocated Zeroizing<String>. Rust can't guarantee no other copies; that limit goes in the README.
  • prompt.rs carries #[expect(dead_code)] until init uses it; the expectation fails the build once it is unnecessary.

Every v1.0 command is declared so `pwv --help` shows the full surface;
each one exits 1 with "not implemented yet" until its own PR lands.
Secrets are never accepted as arguments, and PWV_VAULT holds only the
vault path.

Refs #26
Any pwv-core error about the vault's contents exits 3, so scripts can
tell a wrong password or tampered file apart from a missing file (1)
or a usage mistake (2). The core error is found anywhere in the anyhow
chain, so adding context never changes the code.

Refs #26
Secrets are read with rpassword and kept in zeroizing buffers until they
become a SecretString. With --password-stdin every answer is one stdin
line, so tests and scripts can drive the binary without a terminal.

Refs #26
@ericliswan

Copy link
Copy Markdown
Owner Author

Self-review: read the full gh pr diff. Checked the following:

  • No secret can come in through args or env; PWV_VAULT is path-only.
  • eprintln! prints only the error chain, and pwv-core errors never carry secret data.
  • Stdin answers go into zeroizing buffers.
  • The exit-code mapping matches AGENTS.md §2, and DESIGN.md now documents it.
  • No crypto crate is used in pwv-cli.
  • 306 changed lines excluding Cargo.lock.

Left for later PRs: each command's flags and behaviour. Interactive rpassword prompting is only exercised by hand, because CI has no TTY.

@ericliswan
ericliswan merged commit ac8390f into main Oct 27, 2026
4 checks passed
@ericliswan
ericliswan deleted the feat/cli-scaffold branch October 27, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(cli): command-line scaffold

1 participant