Skip to content

Drive a locker over the cable from the CLI - #70

Merged
TheCryptoDonkey merged 1 commit into
mainfrom
feat/cli-serial
Sep 6, 2026
Merged

Drive a locker over the cable from the CLI#70
TheCryptoDonkey merged 1 commit into
mainfrom
feat/cli-serial

Conversation

@TheCryptoDonkey

Copy link
Copy Markdown
Member

notecase could only reach a hardware locker from a browser: the protocol client lived in src/vault.ts but the framing lived in web/src/vaultserial.ts behind Web Serial, which is Chrome-only. So the wallet that holds the seed could not talk to the device that needs it.

That produced a genuinely silly provisioning flow, shipped earlier today: device-node --force printed 64 bytes of bearer material to a terminal, and a second tool carried them to the device — through a clipboard and a shell history on the way.

notecase device provision --mint <host> replaces it. The wallet derives the subtree, sends it down the cable, and raises the device's counter to match its own, in one operation with nothing rendered anywhere. device-node stays for the case where the device is not on this machine.

The move underneath is the substance

web/src/vaultserial.ts was 535 lines of which exactly two exports touched navigator; everything else was bytes in, messages out. That is now src/vaultwire.ts, and the web file is a shim over it — so the browser and the terminal share one implementation of a wire protocol that two devices in the field depend on.

The sixteen existing serial tests pass untouched, which is the evidence this was a move and not a rewrite.

serialport is deliberately not a dependency

It is a native module, a wallet should not need a compiler to install, and almost nobody installing notecase owns a locker. It is imported only when someone asks for the cable, and the failure path was checked before it was installed: a plain instruction, not a stack trace.

device ports runs before the wallet is opened, because listing what is plugged in is a question about the machine rather than about anybody's money — and it is the first thing you reach for when a cable command misbehaves, which is when demanding a PIN is least welcome.

The probe order and its retry are carried over from measured behaviour rather than reinvented: framed first, because a heartwood that receives newline JSON stops answering for the whole session while a vault recovers on the next newline; and twice before moving on, because a board that has just unlocked is still booting.

Verification

Bench-verified against a real heartwood: ports found it, info read it, provision set up moneyer.dev from a throwaway wallet's seed under a held-button card, mints read it back, forget removed it.

Three new tests pin the seam — the frame bytes, that the web module still re-exports the same objects, and that a missing serialport explains itself. 331 pass.

notecase could only reach a hardware locker from a browser: the protocol
client lived in src/vault.ts but the framing lived in web/src/vaultserial.ts
behind Web Serial, which is Chrome-only. So the wallet that holds the seed
could not talk to the device that needs it.

That produced a genuinely silly provisioning flow, shipped earlier today:
device-node --force printed 64 bytes of bearer material to a terminal, and a
second tool carried them to the device - through a clipboard and a shell
history on the way.

notecase device provision --mint <host> replaces it. The wallet derives the
subtree, sends it down the cable, and raises the device's counter to match its
own, in one operation with nothing rendered anywhere. device-node stays for
the case the device is not on this machine.

The move underneath is the substance. web/src/vaultserial.ts was 535 lines of
which exactly two exports touched navigator; everything else was bytes in,
messages out. That is now src/vaultwire.ts and the web file is a shim over it,
so the browser and the terminal share one implementation of a wire protocol
that two devices in the field depend on. The sixteen existing serial tests
pass untouched, which is the evidence this was a move and not a rewrite.

serialport is deliberately absent from package.json. It is a native module, a
wallet should not need a compiler to install, and almost nobody installing
notecase owns a locker. It is imported only when someone asks for the cable,
and the failure path was checked before it was installed: a plain instruction,
not a stack trace. `device ports` runs BEFORE the wallet is opened, because
listing what is plugged in is a question about the machine rather than about
anybody's money - and it is the first thing you reach for when a cable command
misbehaves, which is when demanding a PIN is least welcome.

The probe order and its retry are carried over from the measured behaviour
rather than reinvented: framed first because a heartwood that receives newline
JSON stops answering for the whole session while a vault recovers on the next
newline, and twice before moving on because a board that has just unlocked is
still booting.

Bench-verified against a real heartwood: ports found it, info read it,
provision set up moneyer.dev from a throwaway wallet's seed under a held-button
card, mints read it back, forget removed it. Three new tests pin the seam - the
frame bytes, that the web module still re-exports the same objects, and that a
missing serialport explains itself. 328 pass.
@TheCryptoDonkey
TheCryptoDonkey merged commit e9312ca into main Sep 6, 2026
2 checks passed
@TheCryptoDonkey
TheCryptoDonkey deleted the feat/cli-serial branch September 6, 2026 16:19
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