Skip to content

Land clip selection and secret-heuristic removal on main - #22

Merged
willkotheimer merged 3 commits into
mainfrom
clip-selection
Sep 12, 2026
Merged

willkotheimer merged 3 commits into
mainfrom
clip-selection

Conversation

@willkotheimer

Copy link
Copy Markdown
Owner

PRs #20 and #21 were merged, but not into main: #20 was opened against repeat-unspool and #21 against clip-selection, so both merge commits landed on feature branches and main stayed at #19 (2bfaf65).

This PR carries their commits to main:

The merge base is cf7e206, whose tree is identical to main's HEAD, so this merges without conflicts and adds nothing beyond those two changes.

🤖 Generated with Claude Code

willkotheimer and others added 3 commits September 11, 2026 07:21
A spool is not always wanted whole. Ticking clips narrows what the next serve
delivers and what the whole-spool paste joins, using one working set for both, so
Win+Alt+U, Win+Alt+V and the button cannot come to mean different things.

An empty selection means every clip. Selecting nothing and meaning nothing is not
a state worth having: it would make both hotkeys dead and the button a no-op with
nothing to distinguish that from a bug. Clearing the selection and selecting
everything are therefore the same act, and unticking the last box returns to all
rather than to none.

The selection is not stored. It describes what you are doing now, the way a text
selection does, and one that survived a restart would be a rule the user does not
remember making. It ends when the active spool changes or the spool is cleared,
and drops any deleted clip, because a set still naming a clip that is gone makes
the button promise more than it can deliver.

Serving steps over what is not in play and wraps among the chosen clips. A cursor
left on an excluded clip is not an error — it was put there before the choice was
made — so the next serve walks forward to the first clip in play instead of
refusing.

The button names what it will take: "Put all 15 on the clipboard" becomes "Put 3
of 15", keeping the total visible so a selection reads as a narrowing rather than
as the whole truth.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The heuristics scanned every copy for PEM blocks, JWTs, key prefixes, connection
strings and high-entropy text, and prompted on a match. They went for three
reasons.

They interrupted an ordinary workflow to report something the user already knew.
Copying a credential is a normal thing to do, and the prompt arrived every time,
asking permission for the thing the person had just deliberately done.

The premise was weaker than it looked. The guessing defended against exposure,
but nothing Spool holds leaves the machine. What Spool does change is
persistence: a clipboard entry that would have lived until the next copy instead
lives in an encrypted file with a visible preview. That is a real difference and
it is the honest case for asking — it is not a strong enough one to justify
asking about every API key a developer copies.

And it was the entire cost of capture: 147ms per MiB, because each needle walked
the whole buffer separately. Classification is now 0.003ms, because it no longer
reads the content at all — classify does not take the bytes any more, which is
the strongest form that claim can take. Two tests that failed intermittently at a
five-second timeout stopped being flaky as a side effect.

What is kept is not a guess. CanIncludeInClipboardHistory = 0 is an explicit
statement from the application that owns the secret, and Windows' own Clipboard
History obeys it. Spool makes a transient thing durable, so ignoring it would
persist exactly what a password manager asked it not to, and behave worse than
the OS feature beside it. It costs a flag check.

detect/bytes.ts loses nine functions that existed only to feed the heuristics.
wipe stays: a declined clip must not be left in memory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Stop guessing whether a clip is a secret
@willkotheimer
willkotheimer merged commit dc1bc12 into main Sep 12, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant