Period: July 5 – August 5, 2026 Status: In Progress Depends on: Month 6
The active window. Month 7 clears the work carried over from Month 6 before starting its own scope. The Subscription Manager and email bridge below build directly on the carried-over NWC Proxy Lite and Resend adapter.
- MASTER card account-share — the FOREVER-QR flow on top of the shipped SIMPLE / ONE_TIME activation:
CardClaim/LightningAddressShare/RemoteWalletSharemodel + claim / share-revoke endpoints (enum values already reserved in the schema) - NWC Proxy Lite settlement layer + full LUD-16 / LUD-21 / LUD-22 / NIP-57 closeout — see Month 6 for the full spec
@lawallet-nwc/sdk+@lawallet-nwc/react— delivered. Scope grew past the planned hooks extraction: the SDK went from stub to a full nostr-first client (per-request NIP-98, no JWT in the public surface),@lawallet-nwc/reactships a provider + 12 hooks, cross-origin access was opened (apps/web/proxy.ts) with/api/jwtdeliberately excluded,/api/eventslearned to accept a NIP-98-signed query token, and operators gainedPOST /api/lightning-addressesto issue addresses on a user's behalf. Two reference apps, since moved to lawalletio/sdk:example-onboarding(self-service claim incl. the paid path) andexample-admin-provisioning(operator-issued, proof-of-npub). Docs at/docs/sdk. Remaining: flipprivate: falseand publish to npm.- WordPress plugin (
lawallet-wordpress) - Resend email adapter (foundation for the email-to-Nostr bridge below)
- Nostr scheduler, threat model + security-audit prep, Vercel / Netlify deploy configs
- Subscription Manager — paid tiers (monthly / one-time) with perks: vanity Lightning address, email-to-Nostr bridge, sat allowance
- Nostr Chat — DMs over NIP-17 / NIP-44 (NIP-04 fallback); thread metadata only in DB
- Operator → user broadcast
- Email-to-Nostr bridge —
username+inbox@domain.com→ NIP-44 DM - Internationalization (i18n) —
next-intl, locales at launch:en/es/pt-BR - No new containers — work folds into
apps/webandapps/listener
- Operator-defined subscription plans (price in sats, monthly or one-time, perk flags)
- Subscription purchase via the M6 NWC Proxy Lite; daily expiry cron in
apps/listener - Vanity / premium Lightning address perk
- Email-to-Nostr bridge perk (
username+inbox@domain.com) on the M6 Resend adapter - Sat allowance perk —
TokenAllocationledger credited monthly, consumed by M8 Agents first - Nostr Chat — DMs over NIP-17 / NIP-44 (NIP-04 fallback); messages on relays, only thread metadata in DB
- Operator → user broadcast via instance nsec, segmented by tier
- Internationalization (i18n) — multi-locale UI, operator-selectable default locale
SubscriptionPlanmodel:id, name, slug, priceSats, interval (MONTHLY | ONE_TIME), perks (Json), isActive, createdAt- Perks stored as flags:
vanityAddress,emailBridge,satAllowance(with monthly grant amount) - Admin UI in Settings → Subscriptions: create / edit / archive plans, view active subscribers, revenue summary
- User clicks Subscribe on a plan
apps/webmints an invoice via the M6 NWC Proxy Lite addressed to the operator's wallet- User pays from their NWC wallet
apps/listenerreceives the payment event, POSTs toapps/webwebhookapps/webactivatesSubscription.status = ACTIVE, setsexpiresAt(forMONTHLY), unlocks perks
Subscriptionmodel:id, userId, planId, status, startedAt, expiresAt, lastInvoiceId, createdAt- States:
PENDING → ACTIVE → EXPIRED | CANCELLED - Daily cron in
apps/listenerflips expired subscriptions, revokes perks, optionally DMs the user via instance nsec - Renewal: user re-pays before expiry →
expiresAtextends by interval
- Premium-tier users can claim a 3–7 character vanity address
- Reuses the existing
LightningAddressmodel and resolution path - Vanity claims expire if the subscription expires; address falls back or is freed
TokenAllocationmodel:id, userId, kind (SUBSCRIPTION_GRANT | MANUAL), satsRemaining, satsGranted, sourceSubscriptionId?, expiresAt, createdAt- Monthly cron credits
SUBSCRIPTION_GRANTrows from active subscriptions - Consumed by M8 Agent runs before falling back to NWC payment
- Usage credit only — never converts to spendable Lightning sats
Subscription perk gated by the emailBridge flag.
- Each subscriber gets
<username>+inbox@<domain>as a sub-addressed inbox - MX handled by an inbound provider (Resend Inbound or Amazon SES inbound)
- Provider POSTs parsed email JSON to
POST /api/email-bridge/inbound apps/webvalidates the destination user has an active subscription with theemailBridgeflag- Forwards to
apps/listenervia PG NOTIFY → listener signs a NIP-44 DM with the instance nsec - DM body: subject + plain-text snippet + a link back to the full message in the user dashboard
- Listener publishes the DM to the recipient's preferred relays
- NIP-17 gift-wrapped DMs (default)
- NIP-44 for the inner encryption
- NIP-04 fallback for legacy clients
- Messages live entirely on relays —
apps/webdoes not store message bodies ChatThreadmodel stores per-user thread metadata only:id, userId, counterpartyPubkey, kind (DM), lastSeenAt, muted, pinned, createdAt- Frontend consumes the user's preferred relays directly (NIP-65 relay list); cache-and-display pattern
- New chat surface in the user wallet: thread list, message view, compose
- Admin UI: Communications → Broadcast — pick a segment (active subscribers, all users, single tier), compose, preview
- Backend signs NIP-44 DMs with the instance nsec
- Listener publishes to recipients' preferred relays
- Rate-limited; broadcast log persisted
- Admin dashboard, user wallet, and onboarding wizard translatable
- Public LUD-16 / NIP-05 surfaces remain protocol-only (no UI strings to localize)
- Email templates ship per-locale variants
next-intlfor App Router message catalogs and locale-aware routing- Catalogs under
apps/web/messages/<locale>.json; type-safe keys via generatedMessagestype - ICU MessageFormat for plurals, dates, and sat-amount formatting
- Locale-aware number / currency rendering reuses the M6 default-currency setting
en(source of truth)espt-BR- Community translations via
CONTRIBUTING-i18n.md; missing keys fall back toen
- Per-user preference on
Usermodel (localecolumn), set during onboarding - Operator-defined default via
Settings.i18n.defaultLocale - Anonymous visitors:
Accept-Languageheader → operator default →en - Locale switcher in user wallet settings and admin topbar
- Every user-facing string in
apps/webflows throughuseTranslations() - ESLint rule (
react/jsx-no-literalsscoped to JSX text) prevents regressions pnpm i18n:checkscript verifies key parity across locale files in CI
Namespaced keys added to the existing Settings model:
subscriptions.enabledsubscriptions.defaultGrantSatsemail_bridge.provider—resend | ses | noneemail_bridge.inboundDomainchat.relays— instance default relayschat.allowOperatorBroadcasti18n.defaultLocale—en | es | pt-BR | ...i18n.enabledLocales— whitelist surfaced in the user switcher
- Container count stays at 3 (
web,listener,nwc-proxy) apps/webowns the REST surface and frontendapps/listenergains: subscription expiry cron (daily), email-bridge inbound dispatch, DM publish- NWC Proxy Lite (M6) used for subscription invoice creation
- M6 Resend adapter used for outbound email + the inbound bridge
- Instance nsec used for email-bridge DMs and operator broadcast
/api/admin/subscription-plans—GET,POST(with[id]GET/PATCH/DELETE)/api/subscriptions—GETown list,POSTstart,[id]GET/DELETE(cancel)/api/wallet/allocations—GETuser's allocation balance + history/api/chat/threads—GETlist,[id]GET/PATCH(mute/pin)/api/email-bridge/inbound— webhook from email provider/api/admin/communications/broadcast—POST
| Deliverable | Criteria | Priority |
|---|---|---|
| Subscription plan CRUD (admin) | Operator creates a plan with price, interval, perk flags | P0 |
| Subscription purchase | Pay invoice → Subscription.status = ACTIVE; perk gates flip |
P0 |
| Vanity LN address perk | Premium-tier user can claim a 3–7 char address | P0 |
| Sat-allowance ledger | TokenAllocation credited monthly; visible in user wallet |
P0 |
| Daily expiry cron | Expired subscriptions flip to EXPIRED; perks revoke |
P0 |
| Renewal flow | Re-pay before expiry extends expiresAt |
P0 |
| Settings UI for subscriptions | Toggle + default grant amount editable | P0 |
| Email-to-Nostr bridge | Email to alice+inbox@domain arrives as NIP-44 DM |
P0 |
| Chat DM (NIP-17 / NIP-44) | User-user DM round-trips through user's preferred relays | P0 |
| NIP-04 fallback | Legacy clients can still receive DMs | P1 |
| Operator → user broadcast | Admin sends DM to a segment via instance nsec | P0 |
| i18n — en/es/pt-BR | Locale switcher works; key parity enforced in CI | P0 |
| i18n — operator default locale | Anonymous visitors land in operator-selected locale | P1 |
@lawallet-nwc/sdk |
Full client, no stub warnings; used by both example apps | ✅ P1 |
@lawallet-nwc/react |
Provider + hooks over the SDK; used by docs examples | ✅ P1 |
| Cross-origin SDK access | Third-party apps reach the API; /api/jwt stays same-origin |
✅ P1 |
| Operator address provisioning | Admin/operator issues an address for another npub | ✅ P1 |
| npm publish (both packages) | private: false + published; the last step for consumers |
P1 |
| Group chat threads (NIP-29) | Deferred to M9 / post-roadmap | — |
| Paid relay perk (nostr.wine etc.) | Deferred to post-roadmap | — |
- No NIP-29 group chat threads
- No paid Nostr relay perk (would add a 4th container)
- No Cashu eCash mint or eCash perks
- No agent-as-a-perk wiring — M8 Agents hook into the M7 sat-allowance ledger