Port of Gangleri42 SeedHammer composer + emulator to SeedHammer v1 hardware. Bundles a SeedSigner emulator with QR seed handoff.
Go to file
mineracks 9675c05ff1 emulator: real gui — lift upstream v1.3.0 gui/ + wire Platform
The Phase 2 scaffolding boot screen is replaced with the actual
upstream v1.3.0 gui package running in browser. cmd/emulator now
calls gui.NewApp(plat, version) + drives a.Frame() in a goroutine.

Lifts:
  gui/                         (top-level UI + state machine)
  gui/assets                   (icons, fonts, 9-patch images)
  gui/layout                   (constraint-based UI layout)
  gui/op                       (drawing op primitives)
  gui/saver                    (screensaver + idle timeout)
  gui/text                     (text shaping)
  gui/widget                   (button/menu/keyboard widgets)
All 11 .go files lifted verbatim from seedhammer/seedhammer @ v1.3.0
(commit 2f071c1d...), import paths rewritten seedhammer.com → mineracks.

browserPlatform now implements gui.Platform (12 methods):
  Events(deadline)   — drains v1.Event chan, maps to gui.ButtonEvent
                       (gui.Button enum order matches platform/v1.Button,
                        so the conversion is a direct uint cast)
  Wakeup()           — no-op (no sleep state in browser)
  PlateSizes()       — backup.SquarePlate, backup.LargePlate
  Engraver()         — nullEngraver stub (browser can't punch metal)
  EngraverParams()   — copy of mjolnir.Params {StrokeWidth: 38,
                       Millimeter: 126}. Inlined because tarm/serial
                       (mjolnir's USB dep) doesn't compile to js/wasm.
  CameraFrame(size)  — emits FrameEvent{Error: stubbed} so QR-scan
                       screens fall through cleanly; real handoff
                       lands when SeedSigner sim wires up (Phase 2.5)
  Now()              — time.Now()
  DisplaySize()      — 240×240
  Dirty(r)           — records rect, resets chunk cursor
  NextChunk()        — one-shot: returns full sub-image once per
                       Dirty cycle, then flushes the whole frame
                       buffer to JS via emulatorPaint()
  ScanQR()           — returns nil decodes (stub)
  Debug()            — false

The Engraver interface is satisfied by nullEngraver — Engrave() returns
"engraver not connected" so the GUI's engrave flow fails cleanly
instead of looking like it's working.

WASM grows 2.7MB → 8.0MB (gui package + btcd + crypto deps + fonts
all linked in). Acceptable for the v1 emulator one-time cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 20:53:06 +10:00
address Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
backup Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
bc Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
bezier Lift bezier/ + bspline/ from Gangleri42 fork 2026-05-28 18:29:29 +10:00
bip32 Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
bip39 Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
bspline Lift bezier/ + bspline/ from Gangleri42 fork 2026-05-28 18:29:29 +10:00
cmd emulator: real gui — lift upstream v1.3.0 gui/ + wire Platform 2026-05-28 20:53:06 +10:00
docs Initial skeleton — Phase 1 scaffolding 2026-05-28 18:25:03 +10:00
driver/mjolnir Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
engrave #5 SH1E Pi-side decoder + fuzz harness 2026-05-28 20:41:34 +10:00
font Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
gui emulator: real gui — lift upstream v1.3.0 gui/ + wire Platform 2026-05-28 20:53:06 +10:00
image Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
input Initial skeleton — Phase 1 scaffolding 2026-05-28 18:25:03 +10:00
internal/golden Initial skeleton — Phase 1 scaffolding 2026-05-28 18:25:03 +10:00
nonstandard Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
platform/v1 #4 v1 emulator scaffolding — boots in browser 2026-05-28 20:38:43 +10:00
seedqr Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
web #4 v1 emulator scaffolding — boots in browser 2026-05-28 20:38:43 +10:00
.gitignore Initial skeleton — Phase 1 scaffolding 2026-05-28 18:25:03 +10:00
CREDITS.md Initial skeleton — Phase 1 scaffolding 2026-05-28 18:25:03 +10:00
go.mod Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
go.sum Lift composer substrate from upstream v1.3.0 2026-05-28 18:36:40 +10:00
LICENSE Initial skeleton — Phase 1 scaffolding 2026-05-28 18:25:03 +10:00
README.md Initial skeleton — Phase 1 scaffolding 2026-05-28 18:25:03 +10:00

seedhammer-v1-companion

Status: early development. Phase 1 (composer port) in progress. See docs/architecture/ for the project plan and design docs.

A browser-based companion for SeedHammer v1 hardware (the original Raspberry-Pi-Zero-based engraver), inspired by Gangleri42's SeedHammer II fork. Ships three coordinated tools and one optional desktop wrapper.

What this is

1. Plate composer (browser PWA)

Design a stainless steel seed-backup plate from your phone or desktop — seed words, custom title text, optional logos — then transfer the design to a real SeedHammer v1 controller via QR code.

The composer renders a pixel-faithful preview using the same Go code the Pi controller runs. What you see in the browser is what the engraver will physically punch.

The on-the-wire envelope is SH1E (a CBOR + CRC32 format documented at docs/architecture/sh1e-spec.md). Sized to fit a 24-word multisig plate in a single QR frame, with BBQr fallback for larger multi-plate manifests.

2. SeedHammer v1 emulator (browser PWA)

Run the real v1 controller firmware in your browser. The same gui/, input/, and engrave/ Go packages that drive the physical device, compiled to WASM. Use it to:

  • Test workflows end-to-end without a physical device
  • Take screenshots / record screencasts of v1 flows
  • Demo SeedHammer v1 to people without shipping hardware

Keyboard mapping: arrows = joystick, Enter = center/confirm, 1/2/3 = Button1/2/3.

3. Bundled SeedSigner emulator (browser PWA)

A faithful in-browser SeedSigner — both the classic 1.3" 240×240 model and the newer 2.8" SeedSigner+ "jumbo" model. Generates seed-phrase QR codes that you can hand off to the SeedHammer v1 emulator via a single button press, end-to-end without leaving the page.

Built by hosting the upstream SeedSigner Python code via Pyodide so that the emulator IS the firmware — when SeedSigner releases new versions we bump the pinned commit and the sim updates.

4. Optional Android wrapper

Kotlin/Gradle shell hosting the composer WASM, for users who want a plate-design app instead of a PWA. Mirrors the structure of Gangleri42's SH2 Android companion.

Hardware targeted

This codebase targets the original SeedHammer v1 specifically — the [Pi Zero v1.3 / WaveShare 1.3" 240×240 LCD HAT / MarkingWay engraver] hardware. Not the newer SeedHammer II (RP2040 / TinyGo / SH2E NFC).

For an SH-II companion, use Gangleri42's fork directly. Most of the inspiration for this project comes from there.

Status & roadmap

  • Phase 1 — composer port (Go-to-WASM, SH1E reference encoder, web UI)
  • ☐ Phase 2 — v1 emulator (firmware-in-browser, Gangleri42-faithful UI shell)
  • ☐ Phase 2.5 — SeedSigner emulator + QR handoff
  • ☐ Phase 3 — combined three-pane sim
  • ☐ Phase 4 — real-device validation on real v1 hardware
  • ☐ Phase 5 (optional) — ColdCard emulator (port from Gangleri42's fork)
  • ☐ Phase 6 (optional) — Android wrapper

Building

(Will be documented as Phase 1 lands — go build ./... + a Vite build for the web shells.)

License

Released under the Unlicense (public domain dedication), matching upstream SeedHammer's choice. SeedSigner-derived files segregated and retain their MIT notice.

Credits + provenance

Heavy lifting by three upstreams. See CREDITS.md for what came from where and the pinned baseline commits.