Config file, pinning, and history-DB hardening #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature/config-file"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Cut from
main(was ~1450 lines uncommitted onmainwith no branch).What
$XDG_CONFIG_HOME/breadclip/config.toml, every key defaulted + clamped, unparseable file backed up once. Keys:retention.text/images,panel.width,capture.primary. Missing file = defaults, no error. Example incontrib/config.toml.example.pinnedcolumn (in-place migration), Ctrl+P in the popup,bread.command.clip.pinverb +bread.clip.pinned/.pin.failedevents. Pinned rows are exempt from trim and sort first.capture.primaryadds a secondwl-paste --watch --primaryloop; those entries get aprimarybadge.HistoryErrorso image-write failures surface instead of leaving broken rows, 0600-from-first-syscall image writes, 0700 dirs, orphan-image GC, deterministicid DESCtiebreak.CLIPBOARD_STATE(sensitive/nil/clear/data); sensitive check and content read happen in one event. Fallback path wrapped intimeout -k 2 5.bread_utils::singleton— no stale pid file..jpg/image/jpeg, re-copied as the same type.password:lines, labelled OTPs, Luhn-valid cards, token prefixes). Convenience, not a security boundary.breadclipd.servicesetsPATHso the user service actually findswl-paste.main; release.yml uses${GITHUB_REPOSITORY}.Divergence from the original plan
The original spec framed
ignore_rulesas user-configurable match rules (MIME/app/regex) documented in EVENTS.md. As built it is a fixed secret-detection heuristic with no config surface — simpler, and documented as such in README + module docs. No[ignore]config section exists.The popup panel positioning moved to after realisation so it can measure the panel's natural height — behavioural change, flagged here.
Tests
cargo test --workspace— 42 pass (16 core + 26 daemon).cargo clippy --workspace --all-targets -- -D warningsclean.cargo build --workspaceclean.Manual smoke (not yet done)
capture.primary = true, middle-click select, confirm primary badge.bakwritten, defaults usedPersistence layer changes, all backward-compatible with existing databases via in-place column migration: - WAL journal mode + a 5s busy timeout so overlapping `--capture-once` writers and the popup reader stop dropping captures on SQLITE_BUSY. - `HistoryError` replaces bare `rusqlite::Error` so a filesystem failure while writing an image file surfaces instead of leaving a row that points at a file that was never written. - Image files are created 0600 from the first syscall (O_CREAT|O_EXCL, mode 0600) — no world-readable window before a chmod. Data and images dirs are forced to 0700 on every open. `gc_orphaned_images` sweeps image files no row references (older than 1h, to spare in-flight writes). - `pinned` and `is_primary` columns. Pinned rows are exempt from trim and sort first; `list_entries` breaks timestamp ties by `id DESC` so ordering (and which rows trim keeps) is deterministic within a second. - `Retention` caps are now a field on `HistoryDb` (`open_with`), and `0` is a legal value ("keep no unpinned entries of this kind"). New `config` module: optional TOML at `$XDG_CONFIG_HOME/breadclip/config.toml`, every key defaulted and clamped, unparseable file backed up once (bread-utils tomlcfg discipline). Keys: retention.text/images, panel.width, capture.primary.- Capture now keys off wl-paste's `CLIPBOARD_STATE`: `sensitive` (password manager via `wl-copy --sensitive`, covers the old x-kde-passwordManagerHint case) and `nil`/`clear` persist nothing; `data` reads the content off stdin in the same event, so the sensitive check and the read can't straddle a clipboard change. A manual `--capture-once` with no CLIPBOARD_STATE falls back to querying wl-paste directly, now wrapped in `timeout -k 2 5` so a stalled offer can't block a capture process forever. - Single-instance guard moves to `bread_utils::singleton` (flock) — kernel-atomic, auto-released on death, no stale pid file. - `ignore_rules::is_sensitive`: conservative best-effort heuristics that skip copies which look like secrets even when unflagged — PEM/OpenSSH private key blocks, `password:`-style lines, labelled one-time codes, Luhn-valid card numbers, well-known token prefixes. A convenience, not a security boundary. - `bread.command.clip.pin` verb (payload `{id, pin?}`, pin defaults true) → `set_pinned`, emits `bread.clip.pinned` / `.pin.failed`. - Two independent `wl-paste --watch` loops (regular clipboard always on; primary selection when `capture.primary`), each restarting with 2s→30s capped backoff and state-change-only journal logging. - Image type is sniffed from magic bytes (PNG/JPEG) on the stdin path and requested by actual offered type on the fallback path — a JPEG is stored as `.jpg` / `image/jpeg`, not relabelled PNG. - content_kind: a single line of prose that merely contains a keyword ("class is a concept") no longer classifies as code — it must look like a statement.- EVENTS.md: `bread.command.clip.pin` and `bread.clip.pinned` / `.pin.failed` are now implemented; `select` stays explicitly not implemented with the reason. AGENTS.md follows. - README: Configuration section, Ctrl+P bind, updated privacy notes (CLIPBOARD_STATE + ignore rules), JPEG image entries, primary badge. - check.yml also runs on pushes to `main` — a push to main triggers a dev-track release build, so it should be linted/tested first. - release.yml uses `${GITHUB_REPOSITORY}` instead of a hard-coded `Breadway/breadclip` for the GitHub mirror release upload.