The browser path (no electronAPI) is already a first-class code path:
initializeWithLightning() resolves an EPHEMERAL LocalSigner, allows mock
fallback and leaves debugMode on, so the bill simulator stands in for the
validator. That is what makes a hosted kiosk demo possible at all. Two
things still needed fixing for it.
1. Cursor. `cursor: none` was applied globally for the touchscreen, which in
an ordinary browser reads as a broken page. Scope it to `.kiosk`, set on
<html> by main.ts unless VITE_DEMO_TAG is present — so every real machine
keeps today's behavior and only the demo build shows a pointer.
2. Cleanup. An ephemeral identity per page load is the right call (it isolates
concurrent visitors, and each fresh account gets its own auto-credit under
LNBITS_DEMO_MODE, whereas a single baked-in key would be credited once and
then drain). The cost is a throwaway LNbits account per visit, and nothing
in an auto-created row distinguishes one: pubkey-set/prvkey-NULL equally
describes a real ATM.
A nostr pubkey can't carry a marker — grinding a vanity prefix is far too
slow to do on page load — and the account/wallet the server auto-creates
isn't nameable by the client. So when VITE_DEMO_TAG is set the ATM mints
one extra, never-used wallet whose NAME is the tag, turning the sweep into
an exact string match instead of a heuristic about what looks disposable.
Both are inert on a real machine: the var is unset outside the demo build.
The marker call is fire-and-forget — losing it degrades cleanup, not the demo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013A6683cCHnQxFUosx1krY4
The transport has exposed `create_wallet` (AUTH_ACCOUNT) since the RPC
registry was written, but LnbitsClient never wrapped it — the ATM only ever
needed the auto-created default wallet from `list_wallets`.
Add `createWallet(name)` plus its `CreatedWallet` reply type. Account-scoped,
so the envelope deliberately carries no `wallet_id`: that absence is what
makes the server resolve auth to the Account rather than a Wallet. Not
wrapped in `idempotent()` — a retry would mint a duplicate wallet, same
reasoning as create_invoice.
The reply carries the new wallet's adminkey/inkey, hence the type-level note
not to log it verbatim.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013A6683cCHnQxFUosx1krY4
The machine app's `build` script runs vue-tsc, the Vite build, two electron
tsc passes and an esbuild bundle. Serving the kiosk as a plain SPA needs only
the middle one, and the electron passes drag in native-addon typings that a
web build has no use for.
Add `build:web` (just `vite build`) with a turbo task that still builds the
workspace packages first via `dependsOn: ["^build"]`, so a consumer can run
`pnpm build:web` at the repo root and get `apps/machine/dist`.
`env: ["VITE_*"]` is declared on the task because the Vite vars are baked into
the bundle at build time — without it turbo would happily serve a cached
build produced under different env.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013A6683cCHnQxFUosx1krY4