In Progress
01 / 11
NWC Month 5 · 2026
LaWallet
May 5 — June 5, 2026 · Three themes

Month 5 starts here
Remote Wallets,
cards & polish.

Remote Wallets land first as the named Lightning-source abstraction every card downstream draws from. Then the Card pipeline — QR JWT login, card-installer, the simple-card-manager rewrite, and the end-user "Activate Card" flow. Cards are SIMPLE or MASTER; QRs are ONE_TIME or FOREVER — orthogonal. Topped with the Platform Polish shelf: NIP-05, customizable domain landing, admin home redesign, infra-aware onboarding.

Phase
Expansion
Themes
3 · A → B → C
Headlines
RemoteWallet · Card flow · Landing
Grant
OpenSats · Wave 15
Where M5 fits

An 8-month OpenSats journey,
five months in.

Four months shipped — backend, CI, the admin dashboard, the user wallet + monorepo. M5 lays the abstraction cards depend on, ships the card-side apps end-to-end, and sets up the M6 settlement layer.

M1
Foundation
✓ Shipped
M2
CI · Auth
✓ Shipped
M3
Admin Dashboard
✓ Shipped
M4
Wallet · Monorepo
✓ Shipped
M5
Wallets · Cards · Polish
▶ In Progress
M6
Listener · Proxy · Deploy
Planned
M7
Subscriptions · Chat · i18n
Beyond
M8
AI Agents
Beyond
4 months shipped
3 months ahead
Three themes, one month

Wallets first.
Cards bind to them.

A

Remote Wallets

Lightning-source provider with a type discriminator. NWC active in M5; LND / CLN / BTCPay reserved.

  • RemoteWallet model + driver interface
  • Wallet UI · create / rename / default / disable
  • Connection Map UI · LA + Card ↔ Wallet
  • NWCConnection rows migrate forward
B

Card System Apps & Flows

From blank chip to tap-to-pay — sequenced. QR JWT login is the shared contract both card-side apps build against.

  • B.0 · QR-based JWT login in apps/web
  • B.1 · card-installer Android
  • B.2 · simple-card-manager rewrite + activation QR + rescue
  • B.3 · Activate Card · ONE_TIME / FOREVER QR
C

Platform Polish & Landing

NIP-05, infra-aware onboarding, the customizable domain landing, the admin home redesign, PWA, bug fixes.

  • Full NIP-05 + relay picker + user data cache
  • Customizable domain landing (white-label)
  • Admin home redesign · animated @ hero
  • Onboarding v2 · infra-aware .well-known rewrites
Theme A · Remote Wallets

One model.
Many Lightning sources.

Every Lightning address and every card binds to a RemoteWallet by id. The type field is the door for future drivers — NWC is the only one wired in M5; LND, Core Lightning and BTCPayServer slots are reserved and call sites don't change when they land.

model RemoteWallet { id cuid userId cuid # owner name string # user label type enum # NWC active · LND / CLN / BTCPAY reserved config Json # type-specific, encrypted at rest status enum # ACTIVE | DISABLED | REVOKED isDefault boolean # one default per user createdAt, updatedAt }
Driver interface
getInfo() · makeInvoice() ·
lookupInvoice() · subscribeToPayments()

Adding a new type is a new driver module — no call-site changes in apps/web or apps/listener.

Migration

Existing NWCConnection rows migrate forward into RemoteWallet rows with type = NWC. LightningAddress.remoteWalletId and Card.remoteWalletId replace direct references.

NWC
Nostr Wallet Connect
✓ Ships in M5
LND
LND gRPC / REST
Reserved
CLN
Core Lightning
Reserved
BTCPAY
BTCPay Greenfield
Reserved
Theme A · Connection Map UI

Lightning addresses + Cards
to Remote Wallets, visualised.

The primary surface for editing bindings. Desktop: two-column canvas with sources on the left, wallets on the right, drag-to-rebind. Mobile: three tabs (Addresses · Cards · Wallets), chip-pickers do the rebind.

Desktop · ≥ 1024 px
Lightning addresses
alice@domain
bob@domain
Cards
Card · #042
Card · #043
Remote Wallets
Alby SubNWC
Cashapp BoxNWC
Cold OpsNWC
LA · binding
Card · binding
Mobile · < 1024 px
Addresses
Cards
Wallets
alice@domainAlby Sub
bob@domainCashapp Box
vip@domainCashapp Box

Tap chip → bottom-sheet picker rebinds. Wallets tab is read-only summary; rebinds happen from Addresses / Cards.

Theme B · Card pipeline

From blank chip to tap-to-pay
in five sequenced steps.

B.0 ships first — the shared QR JWT login both card-side apps consume. Then the field tool, the admin workbench, the end-user claim, and the full Playwright + simulated-NFC happy path.

B.0

QR JWT Login

Shared login surface in apps/web. Backend displays QR; both card apps authenticate against it.

P0 · prerequisite
B.1

card-installer

Android app provisions blank NTAG424s in the field; pair-call carries remoteWalletId.

P0
B.2

simple-card-manager

Rewrite. Drives the E2E. Re-issues activation QR for any card (ONE_TIME / FOREVER) + rescue path.

P1
B.3

Activate Card

End-user wallet scans QR (as new or existing user); ONE_TIME burns + transfers card; FOREVER shares full account.

P0
B.4

Connect E2E

issue → install → activate-QR → claim → pair → pay. Playwright + simulated NFC.

P0
Every card binds to a Remote Wallet from day one
No temporary direct-NWC binding. No rework when LND / CLN / BTCPay drivers land later.
B.0 · QR-based JWT Login

Admin mints the token.
QR carries the JWT.

The admin picks a user, ticks permissions, sets an expiration, and the backend signs a JWT. It renders as a QR on the admin screen; the device scans it and runs with it. Stateless — no session record, no polling, no revocation surface.

Step 01

Pick user

Admin opens Settings → Device Tokens; chooses target user.

Step 02

Tick permissions

RBAC subset checklist — JWT scope ⊆ admin's own RBAC.

Step 03

Pick expiration

Preset list · 1h · 8h · 24h · 7d · custom

Step 04

Generate JWT

POST /api/auth/qr-jwt/generate → signs { sub, scopes, exp } → returns { jwt }

Step 05

QR + scan

Admin UI renders the JWT as a QR. Card app scans → JWT lands in the device.

Single endpoint
/api/auth/qr-jwt/generate · admin only
No storage
No session table · stateless JWT · signature + exp only
No revocation
JWTs valid until exp. Pick short expirations.
Scope-bound
JWT permissions ⊆ admin's RBAC · rate-limited generation
B.2.1 + B.3 · Two orthogonal concepts

Card kind · QR kind.
Separate axes.

A card is declared as SIMPLE or MASTER at creation. A QR issued for a card is either ONE_TIME or FOREVER. Max one active QR of each kind per card — a new QR of the same kind invalidates the previous.

Card kinds · declared at creation
SIMPLE
Single-holder card. Only ownership transfer (ONE_TIME QRs only).
MASTER
Account-share-capable. Supports both ONE_TIME (transfer) and FOREVER (account share) QRs — up to one of each active concurrently.
QR kinds · per activation token
ONE_TIME
Single-use. First scan claims; token burns; transfers card ownership. Works on SIMPLE and MASTER cards.
FOREVER
Multi-use. Every scan grants the claimer share access to the card holder's LAs + Remote Wallets. Never burns. MASTER cards only.
Constraint
Max one active QR per (cardId, qrKind). New QR replaces the previous.
Identity
Claimer = new nsec on the fly · or existing user via NIP-07 / NIP-46 / nsec paste.
Re-issue
Any card holder mints a fresh QR via simple-card-manager. Cards transfer only via their own QR.
Theme C · Platform Polish

The polish shelf
that compounds.

Runs in parallel with the card pipeline. NIP-05 + relay picker + user data cache lay the Nostr-identity surface the listener (M6) will lean on; the customizable landing and admin home redesign reshape first impressions.

NIP-05 — full

.well-known/nostr.json + relays + avatar.

P0

Relay picker

Per-user preference. WSS only.

P1

User Data Cache

kind:0 + kind:10002 with TTL.

P1

Onboarding v2

Infra detect + .well-known rewrites.

P1

Dashboard cache

Dedupe getSettings across admin pages.

P1

PWA Wallet

Manifest + service worker + install.

P1

Domain Landing

White-label entry · cover + isotype + you@domain live.

P0

Admin home redesign

Animated @ hero · LA-first onboarding.

P0

Screenshots + social

In-app strip + social_* footer icons.

P1

Bug fixes

Card-design dropdown · settings dedupe.

P1

Monthly roadmap

Per-month pages on lawallet-landing.

P1

Landing CRM swap

Tally → operator's CRM.

P2
Showcase · Theme C

First impressions,
redrawn.

lawallet-web · landing

Customizable Domain Landing

White-label entry screen. Cover banner, isotype, color + radius tokens, live you@domain preview, optional benefits step, login, continue.

alice@domain.com
1
Claim address · cover + isotype hero
2
Live preview animates as user types
3
Optional benefits markdown step
4
Login → continue to wallet
apps/web · /admin

Admin Home Redesign

Replaces the four-stat-card layout. Animated username @ domain hero, Lightning-address-first onboarding sequence, Remote Wallet picker inline when none is set.

operator@domain.com
1
Step 1 · Claim a Lightning address
2
Step 2 · Connect a Remote Wallet
3
Step 3 · Issue a card
4
Stat cards demoted · zero-states hidden
Onboarding v2 · infra-aware

Copy-pasteable rewrite recipes for /.well-known/*

Detected stackMechanismSnippet hint
CloudflareTransform Rule / Worker/.well-known/* → origin
CF Tunnelcloudflared ingresspath: /.well-known/.*
Vercelvercel.json rewrites/.well-known/:path*
Netlify_redirects/.well-known/* ... 200
Nginxlocation + proxy_passlocation ~ ^/\.well-known/
Caddyhandle_path + reverse_proxyhandle_path /.well-known/*
Apachemod_rewrite [P,L]RewriteRule ^/\.well-known/(.*)$
Direct originDNS A/AAAA points at host— no rewrite needed —

HEAD-probes each path · /lnurlp · /nostr.json · /verify · before the wizard completes. Red/amber/green; cannot finish until all three are green.

3 themes · A → B → C

Let's ship M5.

Remote Wallets first, then the card pipeline, then the polish shelf. The RemoteWallet migration is the lock-in — every card downstream binds to one from day one.

3
Themes, ordered
A · Remote Wallets → B · Card pipeline → C · Polish.
5
B-pipeline steps
B.0 QR login · B.1 installer · B.2 manager · B.3 activate · B.4 E2E.
2 × 2
Card × QR kinds
Cards · SIMPLE | MASTER. QRs · ONE_TIME | FOREVER. Orthogonal.
2
New repositories
card-installer (Android) · simple-card-manager (rewrite).
OpenSats · Wave 15
Dec 2025 — Sep 2026
M5 / 8
navigate 19 jump F fullscreen