Implement clipboard and QR presentation

This commit is contained in:
Hermes Agent
2026-08-10 00:49:42 +00:00
parent b685a4864c
commit b8c614e141
17 changed files with 1368 additions and 18 deletions

View File

@@ -26,6 +26,9 @@ key_material = "keys"
# accepted and split without launching a shell.
editor = ["code", "--wait"]
# Optional; upstream pass defaults to 45 seconds. Values are limited to 1..300.
clipboard_timeout_seconds = 45
[[git.remotes]]
name = "origin"
url = "https://git.example.test/alice/password-store.git"
@@ -49,6 +52,12 @@ store; duplicate names and duplicate reference pairs are errors.
The HTTPS account name is stored inside the protected credential record, not in
TOML. OpenPGP passphrases are addressed by the resolved primary fingerprint.
`clipboard_timeout_seconds` controls the native clipboard presentation lease.
It defaults to 45 seconds for upstream `pass` compatibility and must be between
1 and 300 seconds. The CLI remains alive for the lease so Linux can serve its
selection and every platform can restore or clear the value reliably without a
background helper process.
Passwords, passphrases, tokens, credentials, private keys, and other secret
values are forbidden in TOML. Unknown fields are rejected. Parse errors never
echo the source line or value, so an accidentally supplied secret is not

View File

@@ -16,7 +16,9 @@ Normal generation uses the same overwrite decision and `--force` rules as
insert. `--in-place` requires an existing entry, decrypts it, replaces only the
bytes before its first newline, and preserves that newline and every following
byte exactly. The generated password is returned separately as redacted,
zeroizing data for typed terminal, clipboard, or QR presentation.
zeroizing data for typed terminal, clipboard, or QR presentation. Clipboard and
QR channels use the same storage-owned presentation service as `show`, so
generated plaintext is never printed for those modes.
The completed entry is encrypted for the nearest recipient policy and written
atomically. Embedded Git receives `Add generated password for ...` only after a

46
docs/presentation.md Normal file
View File

@@ -0,0 +1,46 @@
# Clipboard and QR presentation
`crates/storage` owns secret-presentation lifecycle and derived QR data. The
CLI receives an already selected `PresentationSecret` or generated password and
only writes storage-produced status or terminal-rendering bytes. It never
launches `xclip`, `wl-copy`, `pbcopy`, `qrencode`, an image viewer, or a shell.
The same byte-oriented APIs are ready for pass-otp code and URI results without
placing OTP parsing or calculation in a frontend.
## Clipboard lifecycle
The default clipboard lease is 45 seconds, matching upstream `pass`.
`clipboard_timeout_seconds` may configure 1 through 300 seconds. Before copying,
storage snapshots an existing UTF-8 clipboard value. At lease completion or
cancellation it reads the clipboard again:
- if the copied secret remains current, storage restores the previous text or
clears a previous empty/non-text clipboard;
- if another application or the user supplied newer contents, storage leaves
those contents untouched;
- if cleanup cannot be verified or completed, the operation returns a typed
cleanup failure rather than claiming success.
The CLI deliberately remains alive during the lease. This lets X11 and Wayland
serve the selection and guarantees cleanup without spawning a daemon or helper.
Linux also marks copied text with the commonly supported password-manager hint
to discourage clipboard-history retention. macOS uses NSPasteboard, Windows
uses the native clipboard, and Linux uses direct X11 or Wayland data-control
protocols through the safe `arboard` adapter. Unsupported targets return a
typed unavailable result. Tests inject a byte-oriented backend and cover
expiry, restore, clear, cancellation, failure, and newer-content races without
touching the developer clipboard.
## QR data
`QrMatrix` encodes secret bytes with the pure-Rust `qrcode` implementation and
returns only dimensions and dark/light module queries. Its module buffer,
terminal rendering, and source payload all use zeroizing/redacted storage
objects. The terminal renderer supplies the standard four-module quiet zone,
square half-block cells, and no plaintext label or payload. Frontends can draw
the same matrix natively without regenerating or interpreting it.
Round-trip tests decode generated password, Unicode, and `otpauth://` matrices
with the test-only `rqrr` decoder. Empty and oversized payloads are typed errors,
and CLI tests prove `show` and `generate` clipboard/QR modes never emit their
plaintext values.

View File

@@ -23,6 +23,9 @@ 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.
The resulting secret is consumed by the shared presentation service documented
in [`presentation.md`](presentation.md); plaintext is not written to CLI output
for clipboard or QR requests.
## Name and plaintext search