|
| 1 | +# AGENTS.md — rpass |
| 2 | + |
| 3 | +CLI password manager (Rust). Optimize for short context: read this first, then only the files you need. |
| 4 | + |
| 5 | +## Stack |
| 6 | + |
| 7 | +- Rust edition **2021**, package `rpass` (`src/lib.rs` + bin `src/main.rs`) |
| 8 | +- CLI: **clap** derive · crypto: **argon2** + **aes-gcm** + **rand** · secrets wipe: **zeroize** |
| 9 | +- Errors: **thiserror** (`error::Error` / `error::Result`) — avoid bare `unwrap` in lib paths |
| 10 | +- Serde JSON vault file · config TOML · storage under XDG dirs |
| 11 | + |
| 12 | +## Layout (where to edit) |
| 13 | + |
| 14 | +| Path | Responsibility | Touch when… | |
| 15 | +|------|----------------|-------------| |
| 16 | +| `src/cli/mod.rs` | dispatch `run()` | wire new commands | |
| 17 | +| `src/cli/args.rs` | clap structs / enums | flags, subcommands | |
| 18 | +| `src/cli/handlers.rs` | `cmd_*` handlers | command behaviour | |
| 19 | +| `src/cli/prompt.rs` | master-password prompts, `open_vault` | UX prompts | |
| 20 | +| `src/cli/clipboard.rs` | clipboard copy / clear | clipboard UX | |
| 21 | +| `src/cli/repl.rs` | interactive mode | REPL commands | |
| 22 | +| `src/vault.rs` | unlocked vault API, CRUD, audit, import/export, tables | business ops on entries | |
| 23 | +| `src/crypto.rs` | KDF, AES-GCM, password gen, TOTP, strength, HIBP, b64 | crypto behaviour | |
| 24 | +| `src/wordlist.txt` | passphrase dictionary | Diceware words | |
| 25 | +| `src/storage.rs` | FileStore, seal/unseal, session, backups, paths | persistence / session / paths | |
| 26 | +| `src/models.rs` | `Entry`, `VaultData`, `MasterKey`, audit types | data shape / serde fields | |
| 27 | +| `src/config.rs` | TOML config + env + `VaultPrefs` | defaults, keys, multi-vault map | |
| 28 | +| `src/error.rs` | error enum (`Display` via i18n) | new failure modes | |
| 29 | +| `src/i18n.rs` | EN/FR UI language (`t` / `tfmt!` / `init`) | new UI strings | |
| 30 | +| `src/main.rs` | parse → `cli::run`, exit codes | exit mapping only | |
| 31 | +| `tests/vault_flow.rs` | library lifecycle tests | vault API regressions | |
| 32 | +| `tests/integration.rs` | binary smoke tests | CLI surface smoke | |
| 33 | +| `completions/*` | shell completion scripts | new top-level commands | |
| 34 | + |
| 35 | +Do **not** invent parallel storage layers. Extend `VaultStore` / `FileStore` if needed. |
| 36 | + |
| 37 | +## Design invariants (do not break) |
| 38 | + |
| 39 | +1. **Disk always encrypted**: plaintext `VaultData` exists only in memory after unlock. Mutate → `Vault::save()` → `storage::seal` (AES-GCM, **fresh nonce** each write). |
| 40 | +2. **File format** `EncryptedVault`: `version`, `salt` (b64), `nonce` (b64), `ciphertext` (b64+tag), `written_at`. Atomic write: `*.tmp` + rename; Unix mode `0600`. |
| 41 | +3. **Key hierarchy**: master password + salt → Argon2id (`hash_password_into`) → 32-byte `MasterKey` (zeroized). |
| 42 | + Session (P0): master key scellée dans `data_dir/.{name}.session` ; **wrap key dans `runtime_dir/{name}.wrap`** (`$RPASS_RUNTIME_DIR` ou `$XDG_RUNTIME_DIR/rpass`, sinon `data_dir/runtime/`). Jamais les deux dans le même fichier. `vault_path` validé ; `is_session_active` ne déchiffre pas / ne prolonge pas. |
| 43 | +4. **Decrypt errors**: `crypto::decrypt` → `DecryptFailed` ; `storage::unseal` (MDP saisi) mappe vers `WrongMasterPassword`. Session invalide → purge + `VaultLocked`. |
| 44 | +5. **Write lock**: `FileStore::save_encrypted` prend un lock coopératif `{name}.vault.lock` (PID) ; erreur `VaultBusy` si concurrent vivant. |
| 45 | +6. **Entry keys** in map: lowercase service name (`VaultData::entry_key`). Display name keeps original casing. |
| 46 | +7. **Password change** goes through `Entry::set_password` (history, max 10, updates `password_changed_at`). |
| 47 | +8. **Unlock**: `Vault::open_or_prompt` / session first — do not require password if session valid. |
| 48 | +9. **Clipboard**: effacement non garanti en CLI one-shot ; `RPASS_CLIPBOARD_WAIT=1` force l'attente. REPL OK. |
| 49 | +10. **No exploit PoCs, no weak crypto “for tests”** in production paths. Tests may use temp dirs via `RPASS_DATA_DIR` / `RPASS_CONFIG` / `RPASS_RUNTIME_DIR`. |
| 50 | + |
| 51 | +## Default paths |
| 52 | + |
| 53 | +- Vault: `$RPASS_DATA_DIR` or `dirs::data_dir()/rpass/{name}.vault` → typically `~/.local/share/rpass/default.vault` |
| 54 | +- Config: `$RPASS_CONFIG` or `~/.config/rpass/config.toml` |
| 55 | +- Session meta: data dir `.{name}.session` |
| 56 | +- Session wrap key: runtime dir `{name}.wrap` |
| 57 | +- Backups: data dir `backups/{name}_{timestamp}.vault.bak` |
| 58 | +- Env overrides: `RPASS_VAULT`, `RPASS_VAULT_PATH`, `RPASS_DATA_DIR`, `RPASS_RUNTIME_DIR`, `RPASS_TIMEOUT`, `RPASS_PASSWORD_LENGTH`, `RPASS_LANG` (`en`/`fr`), `RPASS_CLIPBOARD_WAIT`, `NO_COLOR` |
| 59 | + |
| 60 | +## Commands map (CLI ↔ logic) |
| 61 | + |
| 62 | +- Vault mgmt: `init|unlock|lock|change-master-password|status|destroy` → `Vault::*` + session in `storage` |
| 63 | +- CRUD: `add|get|show|list|update|delete|search` → `Vault` + `vault::print_*` |
| 64 | +- Gen: `generate` / auto on `add` → `crypto::generate_password` / `vault::gen_password_from_opts` |
| 65 | +- Audit: `audit|check-breach|strength|expire` → `Vault::audit`, `crypto::{check_breach,password_strength}` |
| 66 | +- TOTP: `add-totp|totp` → `Vault::{add_totp,totp}` / `crypto::generate_totp*` |
| 67 | +- I/O: `export|import|backup|restore` → vault import/export helpers + `storage::{create_backup,restore_backup}` |
| 68 | +- Config/REPL/completions: `config|*`, `interactive`, `completions` |
| 69 | + |
| 70 | +Global flag: `--vault <name>` (not short `-V`; version uses `-V`). |
| 71 | + |
| 72 | +## Conventions |
| 73 | + |
| 74 | +- Errors: add variants in `error.rs`, return `Result<T>`, map at boundaries in `cli.rs`. |
| 75 | +- UI strings: English default; French via `RPASS_LANG=fr` / `config.language` using `crate::i18n::{t,tfmt,init}`. |
| 76 | +- Display tables: `colored` / `comfy-table`; respect password masking unless `--show`. |
| 77 | +- Clipboard: best-effort (`arboard`); failure must not abort core ops. |
| 78 | +- Sensitive types: `MasterKey`, `MasterPassword` use `Zeroize`/`ZeroizeOnDrop` — keep secrets out of `Debug` logs. |
| 79 | +- Prefer small functions; keep clap structs and handlers in `cli.rs` (large file — jump via command name / `fn cmd_`). |
| 80 | +- Imports: std → external → `crate::`. |
| 81 | + |
| 82 | +## Verify before done |
| 83 | + |
| 84 | +```bash |
| 85 | +cargo test |
| 86 | +cargo build # or --release for CLI smoke |
| 87 | +./target/debug/rpass --help |
| 88 | +``` |
| 89 | + |
| 90 | +Targeted: |
| 91 | + |
| 92 | +```bash |
| 93 | +cargo test --test vault_flow |
| 94 | +cargo test --lib crypto |
| 95 | +``` |
| 96 | + |
| 97 | +Need isolated vault in tests: set `RPASS_DATA_DIR` + `RPASS_CONFIG` to a temp dir (see `tests/vault_flow.rs`). |
| 98 | + |
| 99 | +## Out of scope (unless asked) |
| 100 | + |
| 101 | +Multi-user/sync-cloud, team hybrid crypto, full i18n, generated man pages, clap_complete crate (completions are static under `completions/`). |
| 102 | + |
| 103 | +## Token hygiene for agents |
| 104 | + |
| 105 | +- Prefer **surgical reads** of one module; for CLI: `args.rs` (flags) → `handlers.rs` (`fn cmd_`) → `repl.rs` if REPL. |
| 106 | +- After behavior change in vault/crypto/storage: update or add a test in `tests/vault_flow.rs` or unit tests in the same module. |
| 107 | +- Do not re-document the whole README in PRs; change README only if user-facing paths/commands change. |
| 108 | +- Avoid drive-by refactors and new heavy deps without need. |
0 commit comments