Raspberry/Pi software for nSealr signing devices.
This repository owns the Raspberry/Pi implementation family. The first line is the SeedSigner-style QR vault flow for Nostr: scan an unsigned event request, review on a trusted display, approve with physical controls, sign BIP-340, and return a signed-event QR.
The primary hardware compatibility target is the SeedSigner-style Raspberry Pi Zero kit: Pi Zero-class board, Pi/ZeroCam OV5647 camera, Waveshare-compatible ST7789 240x240 LCD HAT, GPIO joystick/buttons, removable microSD boot media, and a SeedSigner-OS-inspired minimal runtime. Pi 3/4/5 variants can be development or accessibility targets later only if they preserve the same offline QR, local review, physical approval, and RAM-only custody boundary.
The QR vault pattern is shared across signer families. Its common contracts live
in nSealr/specs: QR envelope, review model, review-screen vectors,
approval_digest, identity/policy descriptors, signing vectors, and the
feature conformance matrix at
vectors/features/signer-feature-matrix-v0.json. This repository implements
the Raspberry/Pi side of that pattern; future ESP32 QR vault firmware belongs
in nSealr/esp32. When the Raspberry QR vault and ESP32 QR vault both
support a feature, their final behavior must match the shared contract_id
even if current hardware readiness differs.
- Python package and CLI foundation for one-request signing flows.
- v0
nsealr1:QR envelope encode/decode. - NIP-01 event id computation and BIP-340 signing against shared
nSealr/specsfixtures. - NIP-06 mnemonic derivation for account
0and account-indexed key recovery, checked against the canonical NIP-06 test vector innSealr/specs. - RAM-only session keyring inputs for the current foundation include manual
BIP-39 word entry, SeedSigner Standard SeedQR digit streams, SeedSigner
CompactSeedQR entropy bytes, and plain BIP-39 mnemonic text. Nostr
nsecQR, local mnemonic generation, and local standalone-key generation remain product goals. SeedSigner-compatible import is BIP-39 seed import for NIP-06 Nostr derivation; it does not import Bitcoin descriptors, xpubs, PSBTs, or wallet policy. - The shared
nsealr-account-descriptor-v0routeraspberry_qr_vaultis treated as a stateless-session, manual-only route bound topolicy-manual-only-qr-vaultwithpersistent_grants: false. This repository must not add policy automation, persistent grants, or TROPIC01 to that route. - Explicit approval gate:
sign_eventrequests returnuser_rejectedunless approval is provided to the CLI or signer API. - Deterministic event review model for raw kind, created_at, signer author
pubkey, complete content, and complete structured tags before approval,
checked against every shared
nSealr/specsreview vector. nsealr-vault reviewrenders deterministic review JSON from a request without requiring a secret key or producing a signature. It uses the same request validation as the signing path for host-supplied event fields.nsealr-vault review --output-format screen-jsonrenders deterministic trusted-display page data plus anapproval_digestfor desktop and future Pi display simulations.nsealr-vault review --output-format display-frame-jsonrenders one bounded display frame with deterministic line wrapping/truncation for future LCD adapter tests, checked against sharednSealr/specsreview-display-frame vectors.- The core display package can also render complete constrained-display
detail pages for Event/Content/Tags/Decision, including scroll-window
indicators, compact line styles, long-value continuation indentation, and
visible JSON-style escapes for decoded control characters. Unsupported
glyphs still use explicit
U+XXXXfallback. These are checked against sharednSealr/specsreview-detail-page vectors and exposed bynsealr-vault review --output-format detail-pages-jsonwithout changing theapproval_digestcontract. nsealr-vault sign --approval-digest <hex>can require the signing request to match the previously rendered review-page digest before an approved signature is produced.- Pure-Python physical approval controller for future GPIO adapters. Approval is only accepted after the trusted review pages have been traversed to the final approve/reject page; rejection remains available before the final page.
- Deterministic review transcript generation for future display/GPIO adapter
tests, checked against shared
nSealr/specsreview-transcript vectors, including detail-modeNext/Scrolltraversal for long tag windows. - Hardware-neutral button-driven QR flow boundary for future camera, display, and GPIO adapters.
- Composable hardware adapter boundary that keeps QR scanning, trusted display, physical button input, and response QR output as separate attach points before real Pi drivers are selected.
- SeedSigner-compatible 40-pin GPIO button profile for the Waveshare-style HAT: right advances top-level review pages, down scrolls inside long Content/Tags pages, center press approves on the Decision page, and KEY1 rejects from any page. The profile is testable with injected GPIO and does not claim physical acceptance until run on the Pi kit.
- SeedSigner-compatible ST7789 240x240 layout planner for trusted-review frames. It produces bounded draw commands for title, page indicator, styled body lines, and action hint before a real PIL/spidev display driver exists.
- SeedSigner-compatible driver-facing adapter boundaries for the Pi QR vault: an injected camera-frame QR scanner, an ST7789 review display adapter that applies the bounded layout commands to an injected draw target, and an ST7789 response-QR display adapter that renders an injected QR matrix with a quiet zone. These are hardware-facing boundaries for Pi bring-up, not completed physical acceptance.
- RAM-only secret-provider boundary for the button-driven flow. The key is
loaded for the signing session before review so the trusted screen can bind
the displayed author pubkey into the
approval_digest; signing still only occurs after complete review traversal and physical approval. - CLI
--secret-key-stdin,--mnemonic-stdin,--mnemonic-words-stdin,--seedqr-stdin, and--compact-seedqr-hex-stdininputs forsignandflowkeep desktop simulations closer to the stateless model by avoiding shell arguments and seed files when a caller can provide session key material over stdin. The word-by-word and SeedQR paths reuse package-owned seed-entry validators for future camera/display/button adapters. These are still development harness inputs, not production seed-entry UX. - Hardware-neutral mnemonic seed-entry controller for future Pi display/button adapters. It reads BIP-39 words one by one, normalizes and validates the English wordlist/checksum, derives the NIP-06 session key as a one-shot secret provider, and does not persist the mnemonic.
- Shared nSealr v0 implementation limits for constrained signers, with deterministic rejection of applicable invalid signing-request and QR-envelope hardening vectors before trusted review or signing.
- QR envelope encoding now enforces the same static decoded-JSON byte limit as
decoding, so the Raspberry flow does not emit response QR payloads that v0
readers would immediately reject. It also supports the shared
nsealr1a:animated QR frame set for larger valid responses, with digest, checksum, ordering, and frame-count checks before JSON parsing. nsealr-vault flow --button-sequence ...file-backed physical-button harness that refuses approval until every trusted review page has been traversed and bounds non-terminal button streams before future GPIO adapters exist.nsealr-vault flow --display-frame-log ...records the bounded trusted display frames shown during a button-driven flow for future display adapter acceptance tests.nsealr-vault flow --st7789-layout-log ...records the SeedSigner-compatible 240x240 draw commands derived from each displayed frame for future Waveshare/ST7789 driver acceptance.- The button-driven flow result carries the displayed frame/button/decision
transcript, so adapter harnesses can compare a whole review loop with shared
nSealr/specsreview-transcript vectors before real GPIO/display drivers. nsealr-vault flow --review-transcript-log ...writes that same transcript from the file-backed button harness for cross-repo and adapter acceptance tests.nsealr-vault flow --review-mode detaillets the button-driven harness use complete Event/Content/Tags/Decision detail pages.nextadvances between top-level logical pages,scrollmoves within long Content or Tags windows, and approval is accepted only on the Decision page. The signingapproval_digestremains the sharedscreen-pagesdigest.- JSON and QR file input/output for desktop simulation before camera/display integration.
os/stateless-qr-vault-profile.mdrecords the future Raspberry image acceptance boundary aligned withnSealr/hardware: removable microSD, wireless disabled or absent, RAM-only custody, no swap during signing, no remote access during signing, and no persistent signing-secret storage.- The hardware target is now explicitly SeedSigner-compatible. The first physical target is the available Raspberry Pi Zero; full hardware acceptance still requires the SeedSigner-style camera, ST7789 240x240 display HAT, GPIO controls, and OS profile smoke evidence.
nsealr-vault hardware-probe --out <report.json>writes a non-destructive SeedSigner-compatible Pi Zero probe report for future hardware smoke runs. It checks board model, GPIO/SPI/camera Python modules, camera/SPI boot config markers, swap state, and wireless absence/blocking evidence without claiming production readiness.
- Raspberry/Pi QR-only signing flow.
- Stateless key/session mode.
- Display review for raw event kind, signer author, full content, full tags, and final approve/reject decision.
- SeedSigner-compatible Pi Zero camera/display draw-target integrations and physical GPIO/display acceptance runs.
- Verifiable minimal OS image path.
app/: signer application code.os/: image/buildroot or OS integration notes.docs/: build, hardware, QR, and security documentation.
Run the repository verification loop with:
make ciRun the desktop CLI simulation with:
python3 -m nsealr_vault review --request request.qr --review review.json --input-format qr
python3 -m nsealr_vault review --request request.qr --review review-screen.json --input-format qr --output-format screen-json
python3 -m nsealr_vault review --request request.qr --review display-frame.json --input-format qr --output-format display-frame-json --display-page 0
python3 -m nsealr_vault review --request request.qr --review review-detail-pages.json --input-format qr --output-format detail-pages-json
python3 -m nsealr_vault flow --secret-key <hex> --request request.qr --review review-screen.json --response response.qr --approve
python3 -m nsealr_vault flow --secret-key <hex> --request request.qr --review review-screen.json --response response.qr --button-sequence next,next,next,approve
python3 -m nsealr_vault flow --secret-key <hex> --request request.qr --review review-screen.json --response response.qr --button-sequence next,next,next,approve --display-frame-log display-frames.json
python3 -m nsealr_vault flow --secret-key <hex> --request request.qr --review review-screen.json --response response.qr --button-sequence next,next,next,approve --st7789-layout-log display-layout.json
python3 -m nsealr_vault flow --secret-key <hex> --request request.qr --review review-screen.json --response response.qr --button-sequence next,next,next,approve --review-transcript-log review-transcript.json
python3 -m nsealr_vault flow --secret-key <hex> --request request.qr --review review-detail.json --response response.qr --button-sequence next,next,scroll,next,approve --review-mode detail
python3 -m nsealr_vault flow --secret-key <hex> --request request.qr --review review-detail.json --response response.qra --button-sequence next,next,scroll,next,approve --review-mode detail --output-format qr-animated
printf '%s\n' '<hex>' | python3 -m nsealr_vault flow --secret-key-stdin --request request.qr --review review-screen.json --response response.qr --button-sequence next,next,next,approve
printf '%s\n' '<mnemonic words>' | python3 -m nsealr_vault flow --mnemonic-stdin --account 0 --request request.qr --review review-screen.json --response response.qr --button-sequence next,next,next,approve
printf '%s\n' word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 | python3 -m nsealr_vault flow --mnemonic-words-stdin --mnemonic-word-count 12 --account 0 --request request.qr --review review-screen.json --response response.qr --button-sequence next,next,next,approve
printf '%s\n' '<standard-seedqr-digits>' | python3 -m nsealr_vault flow --seedqr-stdin --account 0 --request request.qr --review review-screen.json --response response.qr --button-sequence next,next,next,approve
python3 -m nsealr_vault sign --secret-key <hex> --request request.qr --response response.qr --input-format qr --output-format qr --approve
printf '%s\n' '<hex>' | python3 -m nsealr_vault sign --secret-key-stdin --request request.qr --response response.qr --input-format qr --output-format qr --approve
python3 -m nsealr_vault sign --secret-key <hex> --request request.qra --response response.qra --input-format qr-animated --output-format qr-animated --approve
python3 -m nsealr_vault sign --secret-key <hex> --request request.qr --response response.qr --input-format qr --output-format qr --approve --approval-digest <hex>
python3 -m nsealr_vault sign --mnemonic-file mnemonic.txt --account 0 --request request.qr --response response.qr --input-format qr --output-format qr --approve
printf '%s\n' '<mnemonic words>' | python3 -m nsealr_vault sign --mnemonic-stdin --account 0 --request request.qr --response response.qr --input-format qr --output-format qr --approve
printf '%s\n' word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 | python3 -m nsealr_vault sign --mnemonic-words-stdin --mnemonic-word-count 12 --account 0 --request request.qr --response response.qr --input-format qr --output-format qr --approve
printf '%s\n' '<standard-seedqr-digits>' | python3 -m nsealr_vault sign --seedqr-stdin --account 0 --request request.qr --response response.qr --input-format qr --output-format qr --approve
printf '%s\n' '<compact-seedqr-hex-bytes>' | python3 -m nsealr_vault sign --compact-seedqr-hex-stdin --account 0 --request request.qr --response response.qr --input-format qr --output-format qr --approve
python3 -m nsealr_vault hardware-probe --out hardware-probe.jsonRaspberry software and tooling are released under the MIT License unless a file says otherwise. SeedSigner is a design reference, not a license source for copied code.