Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

## Unreleased
## Keymaker v2.3.0

One additive format change: a new slot type, `0x03` (FORMAT-V2-DESIGN §4.8),
which a container carries only when its owner chooses it. Every container
Expand Down
9 changes: 7 additions & 2 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,7 @@ Not "later". Cut, with reasons.
|---|---|
| **Plausible-deniability container** and **duress/decoy password** | The blueprint concedes it: "deniability fails if the UI announces it." This is open-source software with a public spec — an adversary who knows Keymaker exists knows the decoy mode exists, and the presence of the feature is itself evidence. Shipping it invites users to bet their physical safety on a property the design cannot deliver. Worse than absent. |
| **PAKE / croc-style transfer** | Needs a rendezvous server. The zero-server property is the product. |
| **Steganography (KEYM-in-PNG)** | The blueprint frames it honestly as "obscurity, not security" — which is the argument for not shipping it. |
| **Steganography (KEYM-in-PNG)** | The blueprint frames it honestly as "obscurity, not security" — which is the argument for not shipping it. The audio carrier that ships under *Audio* (`KAUD1`, `docs/FORMAT-AUDIO-STEGO.md`) is kept for the opposite reason: it is documented as a carrier, not steganography. The magic sits in the first sample LSBs, nothing is hidden from steganalysis, and all confidentiality is the container's, so it makes no claim this row would cut. It is a transport for a container, like paper, and stays only as long as it claims nothing more. |
| **TOTP vault** | Scope creep into password-manager territory, against incumbents with sync. Dilutes focus for no differentiation. |
| **OPFS vault / File System Access workspace** | Chromium-only, large surface, and it puts plaintext-adjacent state into durable storage — directly against "nothing is stored", which is the claim people choose this tool for. |
| **Importers (Hat.sh, age, OpenPGP)** | Low value, ongoing maintenance, and OpenPGP is a key-model mismatch with a huge dependency tree. |
Expand Down Expand Up @@ -1047,7 +1047,12 @@ Phase 4.2 Paper vault print kit ─ done ─ §7.1 parts · the sheet ·
Phase 4.3 Self-extracting page ─ done ─ §7.2 subset · the page · three readers, one file
Phase 4.4 Passkey / WebAuthn PRF slot ─ done ─ §4.7 · reference · parity · UI
Phase 4.5 Inheritance wizard ─ done ─ a plan over shares · paper vault · self-extract · recovery kit
Phase 8 Outreach ────── gated on 6, and on a tag existing
Review End-to-end review fixes ─ done ─ strips scan back in · SVG symbols · secret hygiene · CI signs only reproduced bytes
§4.8 Password-and-shares slot ─ done ─ spec · reference · parity · UI · three fixtures
Paper Paper vault, second pass ─ done ─ set code · presets · version-25 symbols · self-contained strips · printout check
Scanning Photo and live camera ─ done ─ every code in one photo · live camera · TEN-X Bet 1 un-held
Docs RECOVERY.md executed ─ done ─ recovery_test.py runs every bash block · v1, v2, v3 · with shares
Phase 8 Outreach ────── owner-only · drafts in docs/OUTREACH.md · after the next tag
```

**Phase 6 goes before the rest of Phase 4, and that reverses the usual order.**
Expand Down
52 changes: 36 additions & 16 deletions docs/design/TEN-X-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ rule it amends.
The organising fact: Keymaker *displays* QR codes everywhere and cannot *read*
one. The paper vault prints symbols the app cannot scan back. The heir — the
person the product exists for — types. The bets follow from taking that person
seriously, even though the bet that fixes it directly (Heir Mode) is on hold.
seriously. The bet that fixes it directly (Heir Mode) was on hold when this
was written and has since shipped; see the end of this file.

## Decisions already made

- **Bet 1 — Heir Mode (in-app camera scanning) is ON HOLD.** It adds a camera
permission surface and, because `BarcodeDetector` is not available in every
engine, almost certainly a QR-decoding dependency. That is a supply-chain
decision the owner has not taken. Do not start it; do not add a decoder.
- **Bet 1 — Heir Mode (in-app camera scanning) was ON HOLD, and has since
shipped** (`22c7cdf`, every QR code in one photo; `161e919`, live camera
scanning). The hold was the decoder: `BarcodeDetector` is not in every
engine, so a QR-decoding dependency was a supply-chain decision the owner
had not taken. It was taken as `BarcodeDetector` where the engine has it and
`jsqr`, pinned, where it does not.
- **Bet 6's motion amendment is approved** by the instruction to execute every
bet except Bet 1. The house order still applies: amend
`DESIGN-SYSTEM.md § Motion` *first*, in the same PR, then write the code.
Expand Down Expand Up @@ -143,8 +146,8 @@ walks the owner through the *heir's* path.

**Scope.**
- "Rehearse now" on the issued-shares dialog: choose any K of the N shares
just issued, enter them as an heir would (paste; scanning is Bet 1 and on
hold), run the identical decryption via the verify-only path, and report
just issued, enter them as an heir would (paste; Bet 1 was on hold when
this was written), run the identical decryption via the verify-only path, and report
"opened in N s — contents kept hidden". A wrong share fails loudly.
- On success, the next print carries the rehearsal stamp filled in
(date, which strips). The app stores nothing: the stamp is ink on paper and
Expand Down Expand Up @@ -252,12 +255,29 @@ the whole plan; say so in the PR body.
stored anywhere; nothing new touches the clipboard.
- `connect-src 'none'`. The build fails on purpose if it changes.

## Bet 1, for when it is un-held

Heir Mode: in-app scanning of paper parts and shares inside Decrypt, a
viewfinder that requests the camera only on tap, "2 of 3 shares scanned"
progress, arrival detection when someone lands from the printed URL, and
zero-jargon copy. The decision it needs is the decoder dependency
(`BarcodeDetector` where present; a small, licence-vetted fallback where not).
Everything else in this plan is designed so that Heir Mode drops into flows
that already speak the heir's language.
## Bet 1, shipped

As sketched when it was on hold: in-app scanning of paper parts and shares
inside Decrypt, a viewfinder that requests the camera only on tap, "2 of 3
shares scanned" progress, arrival detection when someone lands from the
printed URL, and zero-jargon copy. The decision it needed was the decoder
dependency (`BarcodeDetector` where present; a small, licence-vetted fallback
where not).

What shipped, in `22c7cdf` and `161e919`. The decoder decision was taken as
`BarcodeDetector` where the engine has it and `jsqr` where it does not. On the
Decrypt tab, *Use the camera* and *Scan strips with the camera* open the
device camera from a button and read strips and container symbols held up one
after another; the dialog says what is in and what is still needed ("1 of the
2 needed. Show the next strip."), leaves out a strip from a different set,
stops by itself once there is enough, hands the codes to the same boxes a
scanned photo fills, and releases the camera when it closes. Frames never
leave the page. A photo of a whole sheet is read in one go, on the Decrypt
tab and in the printout check. Code in `src/components/camera-scan.tsx`,
covered by `tests/browser/camera-scan.spec.ts`; the progress logic is gated
by `scripts/camera-progress-test.mjs`.

Not shipped from the sketch: arrival detection when someone lands from the
printed URL, and a separate zero-jargon Heir Mode. The scanner drops into the
existing Decrypt flow instead, which is what the rest of this plan prepared
for.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "keymaker",
"version": "2.2.0",
"version": "2.3.0",
"private": true,
"scripts": {
"dev": "node scripts/build-crypto-worker.mjs && next dev -p 9002",
Expand Down
Loading