Skip to content

v2.3.0

Choose a tag to compare

@agustinkassis agustinkassis released this 06 Aug 22:09
· 20 commits to main since this release

v2.3.0

Release date

2026-08-06

Summary

LaWallet NWC 2.3.0 turns a RemoteWallet into a personal receive proxy: an
eligible NWC wallet can forward every payment it receives to one or more
Lightning Addresses, split by weight, with durable accounting for fees, routing
reserve and partial delivery. Around that it adds per-wallet notifications
over webhooks and Nostr, NIP-57 zap receipts and LUD-21 verification for
wallet-backed invoices, and a protocol capability view that tells an address
owner exactly which of LUD-16, NIP-05, LUD-21, NIP-57 and LUD-12 their address
actually speaks.

Forwarding to an address hosted on the same instance is supported, guarded by
config-time cycle detection and a runtime hop counter rather than by refusing
the case outright. The DEFAULT_NWC address mode is removed — an address now
always names its wallet explicitly instead of inheriting whichever wallet the
primary address happened to point at.

The NWC listener also gains a documented one-command Fly.io deployment.

Highlights

RemoteWallet receive forwarding (#129, #135)

  • One FORWARD receive action per active SEND_RECEIVE NWC wallet, with one
    or more Lightning Address destinations weighted in basis points summing to
    100%.
  • Immutable configuration revisions, receipts, per-destination legs and payment
    attempts. Receipt capture is deduplicated by (walletId, paymentHash) and
    keeps the listener event key.
  • Integer millisatoshi maths throughout, deterministic largest-remainder
    allocation, entry fees, and a 1% + 1 sat routing reserve. Reserve, actual
    routing cost, unused reserve, fee overage and tolerated shortfall are all
    auditable per leg.
  • Amounts too small to forward economically accumulate as pending balance
    instead of being lost, shown as recoverable BLOCKED receipts and retried
    once the grouped amount can be sent.
  • Force Forward safely advances open receipts and wakes the worker, but
    never replaces a PENDING/UNKNOWN payment.
  • Work is claimed through per-wallet leases using FOR UPDATE SKIP LOCKED.
    Every outgoing attempt is persisted before payment with a deterministic
    listener request id; ambiguous payments stay UNKNOWN until a lookup or a
    payment_sent notification proves the result.

Forwarding within the same instance (#135)

  • A destination may be a Lightning Address on this instance. It is resolved
    internally against the instance's own API origin rather than fetched over the
    public internet, so it works even where the public host is not reachable from
    the server.
  • Config-time cycle detection walks the forwarding graph — deferred proxy
    redirects, the address-to-wallet binding, and wallet forward destinations —
    and refuses any destination that leads back to the source. It runs on every
    route that writes a destination, so no single edit can complete a ring.
  • A runtime hop counter stamps each local hop with parent + 1, keyed by
    payment hash so it cannot be spoofed, and stops at three hops before minting
    anything. This covers what the graph walk cannot see: concurrent edits racing
    a cycle into existence, or a restored backup.

Wallet notifications (#135)

  • Multiple per-wallet channels for RECEIVED and FORWARDED events.
  • Webhook delivery accepts only public HTTPS endpoints, pins DNS after a
    private-network check, carries an idempotency key and event key, and stores
    every response. A possibly-delivered webhook becomes UNKNOWN rather than
    being duplicated.
  • Nostr delivery supports a configurable kind, recipient p tag, relays,
    templated content and optional NIP-44 encryption. Retries republish the same
    signed event id.
  • Owner-scoped notification, delivery, pause/resume and retry APIs with live
    SSE refreshes.

Lightning and Nostr protocol support (#135)

  • NIP-57 zaps: zap requests are accepted, persisted, and a signed kind:9735
    receipt is published once the payment settles — via the listener webhook, the
    LUD-21 verify endpoint, or a reconciler catch-up, with lease-based guards so a
    retry cannot publish twice.
  • Zap request validation follows NIP-57 rather than being stricter than it: the
    lnurl tag is optional and accepted in any spelling clients use, and the p
    tag identifies the profile being zapped rather than being required to equal
    the address owner.
  • LUD-21 verification is exposed for RemoteWallet-backed invoices, and
    NIP-57 is advertised only while the listener can observe settlement and the
    receipt signer exists.
  • LUD-12 comments are surfaced on receive data — the payer's own words are
    shown with the payment rather than only being folded into the invoice memo.
  • The instance receipt signer is created and published as root NIP-05 identity
    _. .well-known NIP-05/LNURL-pay, LUD-16 callbacks and the LUD-21 verifier
    are publicly readable with CORS and preflight support.

Protocol capability view (#135)

  • The address list gains a Protocols column and the address page reports all
    five protocols, each an interactive badge: hover for a summary, click for a
    description, use cases and the spec link.
  • Capabilities follow the payment. An ALIAS address reports what its alias
    target
    supports, a deferred proxy reports the proxy's, and a bound wallet
    reports this instance's. NIP-05 is always this domain's, since it publishes
    the name rather than routing the payment.
  • Alias capabilities are probed when the redirect is saved and stored on the
    address, so listing many addresses never reaches out to their targets.
    Unsupported and not-yet-checked are kept visually distinct.

DEFAULT_NWC address mode removed (#135)

  • An address in this mode routed through "whatever wallet the primary address
    happens to point at", so its destination could change behind the owner's back
    whenever the primary moved. Addresses now always name their wallet explicitly.
  • New addresses inherit the account's active wallet as their own binding, so
    onboarding still produces an address that works immediately.

Fly.io listener deployment (#130, #131, #132, #133, #134)

  • pnpm deploy:fly provisions the four required secrets and deploys
    apps/listener with the flags the service needs, from a documented
    apps/listener/fly.toml.
  • The listener adopts shared vault secrets from Vercel so web and listener
    cannot drift apart, and the install is documented at /docs/deploy/fly.

Send-screen recipient suggestions (#138)

  • The /wallet/send recipient field now shows the last 10 saved recipients
    while it is empty, and the first keystroke replaces them with suggestions
    derived from what was typed, so recents and typed matches no longer compete
    for the same space.
  • Native browser autofill and password-manager dropdowns are suppressed on that
    field, which previously rendered on top of the custom suggestion list.

Upgrade notes

  • Run the migrations. This release adds eleven, applied automatically by the
    web image or with
    pnpm --filter @lawallet-nwc/web exec prisma migrate deploy.
  • Take a database snapshot first. Unlike the additive ones,
    20260806060000_remove_default_nwc_address_mode rewrites address rows: every
    DEFAULT_NWC address is bound to the wallet it resolves to today, and any with
    no active wallet to bind becomes IDLE. Payments keep landing where they did,
    but the change is not trivially reversible.
  • The listener is required for automatic receive forwarding and NIP-57
    receipt publication. Keep NWC_VAULT_SECRET identical in web and listener,
    then start and health-check the listener before relying on either.
  • Configure or verify the instance NIP-57 receipt signer in Admin ▸ Settings ▸
    NWC Services
    . Its public key is published at /.well-known/nostr.json?name=_.
  • Forwarding is disabled by default on existing wallets. Enable it
    deliberately per eligible RemoteWallet and configure destinations totalling
    100%.
  • Lightning Addresses aliased before this release report their protocols as
    "not checked" until their redirect is saved again, which runs the probe.

Documentation

  • Owner and operator runbook: docs/services/REMOTE-WALLET-FORWARDING.md
  • Architecture reference:
    apps/docs/content/docs/architecture/remote-wallet-forwarding.mdx
  • Fly.io install guide: apps/docs/content/docs/deploy/fly.mdx