Config file, pinning, and history-DB hardening #1

Merged
Breadway merged 4 commits from feature/config-file into main 2026-08-31 18:14:16 +08:00

4 commits

Author SHA1 Message Date
Breadway
6db4a96026 breadclip: document pin verb, config file, and ignore rules
All checks were successful
check / check (push) Successful in 2m9s
- 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.
2026-08-31 15:07:35 +08:00
Breadway
d308593efd breadclip: pin toggle, primary/pin badges, config-driven panel
- Ctrl+P pins/unpins the selected row, re-reads history (pinned rows
  sort first), rebuilds the list and re-selects the toggled row.
- ★ badge on pinned rows, "primary" badge on primary-selection rows.
- Panel width comes from `config.panel_width`; positioning now runs
  after the panel is realised and measures its natural height instead
  of always assuming the worst-case estimate, so a short history gets
  a correctly-anchored short panel.
- Delete in the search box edits the query again instead of deleting
  the selected history row.
- Re-copy offers an image row as its stored MIME type (JPEG stays
  JPEG). History fetch is a flat cap since pinned rows can exceed
  retention.
2026-08-31 15:07:35 +08:00
Breadway
83630033cd breadclipd: watch-driven capture, flock singleton, ignore rules, pin verb
- 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.
2026-08-31 15:07:22 +08:00
Breadway
967fd9f1b7 breadclip-core: history DB hardening + user config + pin/primary schema
Persistence 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.
2026-08-31 15:07:04 +08:00