feat(secrets): first-party keyring commands and secrets panel - #448
duketopceo wants to merge 8 commits into
Conversation
Panel gains vault dropdown with per-service counts, name/recent sort, external-credential badges, coalesced copy, and a 30s conditional clipboard clear via omarchy-secrets-clipclear, which clears only while the clipboard still holds the copied secret. Commands: set stamps app=omarchy on create and updates every duplicate of a service/account pair; delete removes every match so copies left by other tools cannot keep the credential readable; list emits app.
…uards - Re-arm setProc.stdinEnabled per run; a finished process kept the closed channel so every add after the first starved the child's stdin read. - Copy is two-stage (v=$(get) && printf | wl-copy) so a failed lookup can neither clobber the clipboard nor masquerade as a successful copy. - The 30s clipboard clear is a detached systemd --on-active timer: it survives copy-then-dismiss, and clipclear still re-verifies the clipboard holds that secret before clearing (now against every same-pair value). - Delete runs clipclear first, while the keyring copy still exists to compare against, then removes every matching item. - stderr surfaces only on nonzero exit; success diagnostics like "updated 1 item(s)" no longer flash as errors. - Process exits after close() no longer repopulate or notice a wiped panel; a stall timer names a locked-keyring hang instead of spinning. - Keys and clicks respect the vault picker: no hidden-list activation, and a click copies the clicked row rather than the keyboard selection. - The all-vaults state is a null sentinel; a service named "*" stays filterable. Picker rows virtualize. - set/delete tolerate per-item keyring errors and report partial counts; clipclear is hidden; list sort tolerates null labels. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The keybind footer Text had no width bound or elide — at the 560px card it painted past the right edge, clipping `esc close`. Tightened separators and added ElideRight so hints stay inside at any width. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Independent review (AI-assisted, Claude). Read the five commands, the panel's process handling and both tests; ran the QA set in an Ubuntu 24.04 arm64 container. I could not exercise the QML panel against a live shell. Scope question for the maintainers Nothing here is Mac-specific (python-gobject over Secret Service, quickshell panel). The same design was proposed in discussion #447 with no replies yet. Since Dependencies are already in the base set
One real defect: trailing newline breaks the timed clipboard clear
Net effect: any secret created from the CLI with a trailing newline is copied without it and then never auto-cleared. Secrets added through the panel form are unaffected ( Smaller observations (non-blocking)
Local verification (Ubuntu 24.04 arm64 container)
|
…es in clipclear Review on omacom#448 found the timed clipboard clear never fired for echo-created secrets: set stored stdin verbatim (with newline) while command substitution stripped it on copy, so clipclear's exact match failed. set now strips one trailing newline (matching omaseal set), and clipclear also accepts stripped-form matches for entries stored before the normalization. Also disclose on overwrite when an update touches items not stamped app=omarchy, mirroring the 'Managed by' disclosure on delete.
|
Thanks for the thorough review — the trailing-newline defect was a real one and is fixed in Defect — fixed. Foreign-item overwrite disclosure — added. Scope question — agree it's a maintainer call. Position from this side: nothing here is Mac-specific by design (python-gobject + Secret Service + quickshell, all already in Noted, unchanged:
|
A locked collection listed as an empty vault ("No secrets stored") and
gave the user no way forward. Worse, the list call itself can spawn a
prompter — which this Overlay-layer, exclusive-keyboard-grab window
covers and starves of input.
Add omarchy-secrets-status (read-only lock probe) and
omarchy-secrets-unlock (blocking unlock via the system prompter). The
panel probes status before listing, shows "Keyring locked" with an
Unlock action (button or u key) instead of the empty-vault text, and
refuses add while locked. requestUnlock detaches the call and dismisses
the panel so the prompter gets the screen and keys; the next summon
sees the unlocked keyring.
Omarchy gains a first-party way to browse and manage the system Secret Service keyring — Setup → Security → Secrets opens a panel over the shared
org.freedesktop.Secretnamespace, so credentials written by keytar-based editors, GitHub CLI, Seahorse, or omaseal are visible and manageable in one place for the first time.Related: #447
What ships
bin/omarchy-secrets-{get,set,list,delete,clipclear}— python-gobject commands over D-Bus.setreads the value from stdin (never argv) and updates every item matching a service/account pair;listprints JSONL metadata only, never secret values;deleteremoves every match and re-verifies;clipclearclears the clipboard only while it still holds the addressed secret.shell/plugins/secrets(omarchy.secrets) — virtualized list, vault dropdown with per-service counts, name/recent sort,/filter,ext/other appprovenance badges, keyboard-first (j/k, Enter copies,xconfirms-delete,v/s/r/a).gibackend (no keyring needed).Design decisions
DONT_MATCH_NAMEgeneric schema onservice/accountso items other tools wrote are found; new items are stampedapp=omarchyand foreign items carry a badge plus a "Managed by X" disclosure on delete.set/deletedeliberately act on every matching item so duplicates written by different tools stay coherent rather than silently diverging.v=$(omarchy-secrets-get …) && printf %s "$v" | wl-copy --sensitive— two-stage so a failed lookup cannot clobber the clipboard or report false success. The add form writes to the child's stdin after start (stdin is re-armed per run — a finished Process keeps its closed channel).systemd-run --user --on-active=30unit rather than an in-panel timer, so copy-then-dismiss still clears; clipclear re-verifies the clipboard contents against every same-pair value before clearing, so a newer user copy is never destroyed.updated 1 item(s)are not errors), a stall timer names a locked-keyring hang instead of spinning forever, and process exits after close cannot repopulate a wiped panel.Verification
test/shell.d/secrets-commands-test.sh: 24 behavioral assertions against a stubbed Secret Service (round-trip, duplicate coherence, provenance stamp, conditional clear, post-delete verify).test/shell.d/secrets-panel-test.sh: 45 source-contract assertions (identity capture before async exit, stdin re-arm, picker/key guards, detached clear).bin/omarchy commands --check: passes (474 commands);./test/cli: clean;./test/shell: clean except pre-existing environmental failures that need a siblingomarchy-pkgscheckout (identical on a clean tree).Notes for reviewers
omarchy-secrets-clipclearis# omarchy:hidden=true(panel-internal helper, like the clipboard paste helpers).--only-omarchymode instead.Generated with Devin
Model.js(sibling-panel convention) for behavioral unit tests;pendingCopyre-reads the selection at fire time (a moved selection copies the new row — arguably correct); non-UTF-8 secrets can't round-trip through the clipboard compare.