Skip to content

sidestr browser signer (window.nostr.sidestr.signTransaction), security-key fixes, 0.0.9 - #27

Merged
jjohare merged 6 commits into
JavaScriptSolidServer:mainfrom
jjohare:sidestr-signer
Sep 24, 2026
Merged

jjohare merged 6 commits into
JavaScriptSolidServer:mainfrom
jjohare:sidestr-signer

Conversation

@jjohare

@jjohare jjohare commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

Podkey as the reference signer for sidestr's browser-signer proposal (sidestr/spec#16), plus fixes for security-key unlock and a look-and-feel pass.

sidestr spends

  • window.nostr.sidestr { version: 1, name, signTransaction({ chain, tx }) }. Refusals carry code: rejected, unsupported, not-yours, invalid or unavailable.
  • The spend window (popup/spend.*, src/sidestr/):
    • reads the chain from its id (announcement, then the signer's mirror, then every block validated) and pins each chain's signer on first spend;
    • signs only your own mature, unspent coins, and computes every sighash itself;
    • shows recipients (npub, plus profile name when found), what comes back, the fee and a total;
    • reads assets under SPEC 12; a spend that destroys or burns them needs an explicit tick.
  • Every spend asks, whatever the origin is trusted for. The key stays in the background, which signs the window's sighashes once per request.
  • The engine is vendored at pinned commits (scripts/vendor-sidestr.js, with vendor/sidestr/PINS.json holding a SHA-256 per file) because MV3 forbids remote code. There are two recorded patches: the explorer's engine paths now point at the local copies, and the EVM rule is a stub. Test networks only for now.
  • Verified in a browser: in the browser sidecar, the real spend window ran as a page with a stand-in background. It validated sidestr:dreamlab and signed a spend, which was mined at block 431 (7f3a6a63…).

Security keys

  • Passkey windows wait for focus before starting WebAuthn. Unfocused ceremonies failed at once with NotAllowedError, which is the "snag".
  • Transport hints are stored, so unlock goes straight to "insert and touch".
  • Clearer errors with inline retry, three-minute ceremonies, and more algorithms offered.
  • Page requests wait three minutes instead of 30 seconds, so a PIN-and-touch unlock no longer times the site out. Timers are cleared when the answer arrives.

Look and feel

  • Contrast and focus fixes in light and dark.
  • The approval prompt now says that approving trusts the site.
  • Plainer copy.

npm test 218/218, npm run lint clean, npm run build ok. Not yet tried with a physical security key or loaded as a packed extension in Chrome; the spend window was exercised as a page, as above.

🤖 Generated by Claude Code

…le ceremony options

Store the transports a credential reports and pass them in allowCredentials,
so unlock goes straight to the security key instead of the generic chooser.
Name the failed step and what to do (PIN, touch, keep the window in front);
own messages for InvalidStateError, NotSupportedError and SecurityError.
Three-minute timeouts; EdDSA and RS256 offered after ES256. UV stays
'required' on both ceremonies (hmac-secret differs with and without UV).
creationOptions/assertionOptions/cleanTransports exported and tested.

Co-Authored-By: jjohare <github@thedreamlab.uk>
…look and feel

- a ?flow= window waits for focus before its ceremony: WebAuthn refused the
  unfocused new window with NotAllowedError, the main security-key snag
- inline passkey status (step 1 of 2 / 2 of 2, errors with Try again)
  instead of alert(); transports stored in the passkey config
- a window opened only to unlock closes once unlocked
- [hidden] restored over .btn: 'Unlock with passkey' showed with no passkey
- Set up button no longer stuck on 'Waiting…' after a failure
- approve prompt says approving also trusts the site; export shows nsec
- plainer UK copy; text contrast and a dark-mode primary fill that pass;
  focus rings on every control; popup debug logging removed

Co-Authored-By: jjohare <github@thedreamlab.uk>
Implements sidestr/spec proposals/browser-signer.md: any page on any
sidestr chain can ask Podkey to sign a spend without ever holding the key.

- window.nostr.sidestr { version: 1, name, signTransaction({ chain, tx }) };
  refusals carry code: rejected, unsupported, not-yours, invalid, unavailable
- the spend window (popup/spend.*) reads the chain from its id alone
  (announcement -> the signer's mirror -> every block validated), refuses
  mainnet parents and rules it cannot run, pins each chain's signer on first
  spend and refuses a different one, and signs only mature unspent coins
  paying 5120<own key>
- it computes every sighash itself (BIP 341 / unified by parent family) and
  shows recipients (npub, profile name when found), what comes back, the fee
  and the total; assets are read under SPEC 12 (the rule where a chain names
  it, the holders' view where not) and a spend that destroys or burns needs
  an explicit tick
- every spend asks, whatever trust the origin has; the key stays in the
  background, which signs the window's sighashes once per request
- the engine (bitcoin-desktop/schema v0.0.27, sidestr/spec 373d3eb,
  sidestr/explorer db5a04e) is vendored by scripts/vendor-sidestr.js with a
  SHA-256 per file; the explorer points at the vendored copies and the EVM
  rule is stubbed, because MV3 forbids remote code
- provider: request timers are cleared on answer; spends wait 5 minutes
- 0.0.9

Co-Authored-By: jjohare <github@thedreamlab.uk>
…times the site out; docs for sidestr spends

- sendMessageToExtension defaults to 180 s: a request that finds Podkey
  locked waits for the unlock, and a security key's PIN and touch took longer
  than the old 30 s (the site saw a timeout although the unlock succeeded)
- the approval window is tall enough for its trust note
- README: window.nostr.sidestr.signTransaction; architecture; CHANGELOG 0.0.9

Co-Authored-By: jjohare <github@thedreamlab.uk>
…loads

The spend window imports the vendored sidestr engine, so a package without
vendor/ installs but fails on the first spend. The new step reads the
manifest, the three pages and vendor/sidestr/PINS.json and fails the build on
any file missing from the zip.

Co-Authored-By: jjohare <github@thedreamlab.uk>
@jjohare
jjohare merged commit 5ba59c8 into JavaScriptSolidServer:main Sep 24, 2026
1 check 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