Skip to content

fix: bind copies to the visible product; state custody, purposes and clipboard exposure - #5

Merged
turinglambdaai merged 2 commits into
mainfrom
fix/security-ux-20261027
Oct 27, 2026
Merged

turinglambdaai merged 2 commits into
mainfrom
fix/security-ux-20261027

Conversation

@turinglambdaai

Copy link
Copy Markdown
Owner

Implements the interaction contract from #3 and the purpose hints from #2 as one bounded slice, on all three native hosts. Closes #2, closes #3.

What changed

Selection generation guards (issue #3 acceptance: rapid selection). Every selection change bumps a generation counter. entry-list completions and entry-get copies carry the generation they were issued under and are dropped when it no longer matches, and copies are additionally bound to the product name on screen. A stale copy is skipped with a status message instead of landing a secret against a different product header. Store switches invalidate in-flight completions the same way.

Clipboard handling that tells the truth (issue #3 product decision). A copy now reports the real exposure — the raw value is on the system clipboard where any application can read it — and auto-clears after 30 s. The expiry only erases Keyward's own copy: pasteboard changeCount on macOS, a non-secret marker Title on Windows, an ownership-marker mime type on Linux. A later unrelated copy by the user is never erased. OS clipboard history/sync stays disclosed as out of Keyward's control.

Plaintext custody stated in the app. The intro, no-vault, snapshots and About surfaces on all three hosts now say plainly that the vault file and its snapshots are plain text on disk — not OS-encrypted custody — and that the synced folder must be trusted.

Per-entry purpose hints (issue #2). Known entry names carry a one-line consumer note (CI manifest signing / Racket-side updater / macOS host UpdateService / update-manifest key id / Deep Check pairing anchor / commercial-signing lifecycle); unknown names get an explicit confirm-before-delete note. The delete confirmation repeats the purpose line, so the app itself answers "can I delete this?".

Docs updated in the same change: README (both languages), USER-GUIDE (both languages), GUIDE 4.1, CHANGELOG.

Policy acknowledgement (slice boundary)

  • Repository starting SHA: 185637d052d6f3544b7d4989f13b93fb3503c706
  • Central policy read: PRODUCT-STANDARD 79a2b70, DOCUMENTATION-LANGUAGES 311fea2, PORTFOLIO df86808 (repo HEAD 79a2b70), INTERACTION-REVIEW-2026-10 79a2b70
  • Authorization decision: 2026-10-auth-1 — no Permit/Commerce integration; free security/release infrastructure. No payment gate touched.
  • Upstream refreshed before work: rivet local 4383203 (latest), glaze bb98ae6 (latest); CI keeps the release pin v0.11.0. Regenerated typed clients were restored — no schema change in this PR (rivet-schema.json untouched).

Evidence (synthetic data only; no real vault accessed)

  • Engine: raco test tests/ — 69 tests pass; raco make app/backend.rkt compiles.
  • Synthetic acceptance script against the engine: multi-private-key sections stay red when one key is corrupt (no whole-section green), purpose-scoped anchors pair only within their purpose, FPR anchors pair, check output contains zero private-key material, orphans surface as line numbers only — all pass.
  • macOS host: raco rivet build green; 8 s launch smoke against a synthetic vault, process alive.
  • Windows (WinUI 3) and Linux (GTK4) compile evidence comes from CI on this PR; those toolchains are not available on this macOS host. Not claimed as three-platform runtime validation — native interactive acceptance on Windows/Linux remains open for the packaged-evidence slice tracked in security UX: verify every key, clarify plaintext custody and add delivery evidence #3's successor work.

…clipboard exposure

Implements the interaction contract from issue #3 and the purpose hints
from issue #2, one bounded slice, all three hosts.

- Selection generation guards: entry-list completions and copies carry the
  generation they were issued under; a stale result is dropped instead of
  overwriting rows or landing a secret against a different product header.
  Store switches invalidate in-flight completions the same way.
- Copies bound to the on-screen product; the copy message states the real
  exposure (raw value on the system clipboard, readable by any app) and a
  30 s auto-clear that only erases Keyward's own copy: changeCount guard
  (macOS), non-secret marker Title (Windows), ownership-marker mime
  (Linux). A later unrelated user copy is never erased.
- Plaintext custody is stated in the app: intro, no-vault, snapshots and
  About surfaces say the file and snapshots are plain text, not OS-encrypted
  custody, and that the synced folder must be trusted.
- Per-entry purpose hints (issue #2): known update/license/FPR names get a
  consumer line; unknown names get an explicit confirm-before-delete note.
  The delete confirmation repeats the purpose line.

Policy read at this slice boundary: repo start 185637d; PRODUCT-STANDARD
79a2b70; DOCUMENTATION-LANGUAGES 311fea2; PORTFOLIO df86808;
INTERACTION-REVIEW-2026-10 79a2b70; authorization decision 2026-10-auth-1
(no Permit/Commerce, free product). CI rivet pin v0.11.0 unchanged.

Verification: 69 engine tests pass; synthetic acceptance (multi-private
sections, purpose-scoped anchors, FPR pairing, zero value leakage in check
output, orphan line numbers) passes; macOS host builds and survives an
8 s launch smoke against a synthetic vault. Windows/Linux compile via CI.

Closes #2
Closes #3
…er_new_typed

gdk_content_provider_new_for_string does not exist in GTK4; the typed
G_TYPE_STRING provider is what gdk_clipboard_set_text itself builds.
Verified against GNOME/gtk gdkcontentproviderimpl.{h,c} before pushing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant