diff --git a/TODO.md b/TODO.md index 86ef6c0..5a545e4 100644 --- a/TODO.md +++ b/TODO.md @@ -66,21 +66,29 @@ toggle that fits the early-web aesthetic. Add new editor-UX items below. Each has a full spec in `docs/`. -- **Private (encrypted) publishing** — `docs/PRIVATE_PUBLISHING_FEATURE.md`. - Publish any design encrypted, readable only by the author (Phase 1 - "private to me"). The agreed next build; Phase 0 is a validation spike. +- **Zap-gated premium templates (with NWC + platform split)** — + `docs/ZAP_GATED_PREMIUM_FEATURE.md`. **In-progress / current focus.** + Creators mark a template premium with a minimum sat amount; consumers + pay via NWC; the payment is split between the creator (70%) and the + Artstr platform (30%) in one user gesture. Recommended wallet is + CoinOS; any NIP-47 wallet works. Supersedes the older + `ZAP_GATED_TEMPLATES_FEATURE.md` + `ZAPS_NWC_FEATURE.md` drafts. - **Artstr Stacks** — `docs/STACKS_FEATURE.md`. Interactive fullscreen page/card stacks (HyperCard for Nostr) — the Slide Deck extended with - layer actions and an interactive viewer. Queued after private publishing. + layer actions and an interactive viewer. Queued after zap-gated lands. - **Badges (NIP-58)** — `docs/BADGES_FEATURE.md`. Award / display Nostr badges on profile pages. - **Collections (NIP-51 sets)** — `docs/COLLECTIONS_FEATURE.md`. Let users organize favorite community templates into named sets. -- **Zap-gated premium templates** — `docs/ZAP_GATED_TEMPLATES_FEATURE.md`. - Creators mark a template premium with a minimum-zap threshold. -- **Zaps via Nostr Wallet Connect** — `docs/ZAPS_NWC_FEATURE.md`. In-app - zapping without the wallet handoff. (Tracked LNURL-pay tips already ship; - this would be the NWC path.) + +## Phase-3+ work on shipped features + +- **Private (encrypted) publishing — Phases 3 & 4.** + `docs/PRIVATE_PUBLISHING_FEATURE.md`. Phases 1 (private-to-me) and + 2 (toggle public ⇄ private) shipped in commit `3e6e89e`. Still + unbuilt: Phase 3 (share with selected pubkeys) and Phase 4 (NIP-59 + gift-wrap metadata privacy + Blossom/NIP-96 offload for oversized + encrypted blobs). ## Shipped (recent) diff --git a/docs/ZAPS_NWC_FEATURE.md b/docs/ZAPS_NWC_FEATURE.md index a94c79e..fbdada9 100644 --- a/docs/ZAPS_NWC_FEATURE.md +++ b/docs/ZAPS_NWC_FEATURE.md @@ -1,5 +1,12 @@ # Zaps via Nostr Wallet Connect (NWC) Feature Plan +> **⚠️ SUPERSEDED.** This draft is kept for historical context only. +> NWC is no longer being built as a standalone feature — it's the +> wallet substrate for `ZAP_GATED_PREMIUM_FEATURE.md` (which is the +> canonical merged plan). General-purpose NWC zaps for the existing +> tip flow remain a possible follow-up after that ships; see the +> "Open questions" section of the merged doc. + ## Overview Allow users to zap template creators (and specific templates) directly from the community feed using Nostr Wallet Connect (NIP-47). The user connects their Lightning wallet once via diff --git a/docs/ZAP_GATED_PREMIUM_FEATURE.md b/docs/ZAP_GATED_PREMIUM_FEATURE.md new file mode 100644 index 0000000..fb735b8 --- /dev/null +++ b/docs/ZAP_GATED_PREMIUM_FEATURE.md @@ -0,0 +1,317 @@ +# Zap-Gated Premium Templates (with NWC + Platform Split) — Feature Plan + +> **Status:** planned, no code yet. This document supersedes the original +> `ZAP_GATED_TEMPLATES_FEATURE.md` and `ZAPS_NWC_FEATURE.md` — both of +> those described pieces of this feature in isolation. The two have +> been merged here because, in this product, they're a single arc: +> the platform-fee split is the whole reason for shipping zap-gated +> templates, and NWC is what makes the split tolerable as a UX. + +--- + +## Goals + +### Product goal +A creator can mark a template as "premium" with a minimum sat amount. +Other users see a ⚡ lock on the feed card instead of Import. Tapping +the lock pays the creator and the platform in a single user gesture, +and unlocks the import button (cached locally so they don't pay again). + +### Why the split is mandatory +The platform fee is the entire revenue model for this feature. Every +unlock MUST split: creator gets `(100% − fee)`, platform gets `fee%`. +There is no creator-only fallback path. + +### Non-goals (v1) +- **Non-Artstr clients.** Gating is client-side; another client could + in principle implement the `zap-gate` tag without the platform + split. The revenue model relies on Artstr being the primary client + for the audience that publishes premium templates. +- **Refunds.** If the platform invoice fails after the creator + invoice settles, we surface the error and ask the user to retry + the platform leg — no auto-refund. +- **Tipping splits.** The existing LNURL tip flow still goes 100% to + the creator. Splits are scoped to zap-gated unlocks only. +- **Non-NWC payment paths.** v1 requires NWC to be configured. No + bolt11 QR fallback (would re-introduce the two-invoice clunky UX + this whole arc is meant to avoid). + +--- + +## Phased delivery + +The phases build on each other. Each has an explicit ship gate so +work can be paused between phases for review. + +### Phase 0 — Crypto bundle *(~2–3h)* + +The existing codebase signs/encrypts only via `window.nostr.*` (the +user's NIP-07 extension). NWC requires signing kind-23194 events with +the **NWC secret** (a different key, embedded in the wallet URI), +which the extension can't do. So we need a bundled cryptography +implementation. + +**Deliverables** +- Inline a minimal `@noble/secp256k1` + `@noble/hashes` subset + (schnorr sign/verify, ECDH for NIP-04 shared-secret). +- Inline a NIP-04 helper using `crypto.subtle` AES-CBC + the noble + ECDH output. +- Verify against the canonical NIP-04 test vectors in code comments. + +**Ship gate** +Round-trip encrypt/decrypt a known plaintext using the test-vector +keys → produces the test-vector ciphertext. + +--- + +### Phase 1 — NWC connection *(~5–7h)* + +**Protocol summary (NIP-47)** +The wallet exposes a connection URI: +``` +nostr+walletconnect://?relay=&secret= +``` +- `wallet-service-pubkey` — the wallet's NIP-47 service pubkey. +- `relay` — a dedicated relay both ends listen on. +- `secret` — a 32-byte hex privkey that the client uses to sign + requests (this is NOT the user's identity key). + +**Request lifecycle** +1. Build a kind-23194 event: + `content` = NIP-04(`{"method":"pay_invoice","params":{"invoice":"..."}}`) + encrypted to the wallet service pubkey, with the NWC secret as + sender; tag `["p", walletServicePubkey]`. +2. Sign with the NWC secret (schnorr). +3. Publish to the NWC relay. +4. Subscribe `{ kinds: [23195], '#p': [], '#e': [] }`. +5. Decrypt the response with the NWC secret; parse JSON. Possible + results: `{ "result_type": "pay_invoice", "result": { "preimage": "..." } }` + or `{ "error": { "code": "...", "message": "..." } }`. + +**Deliverables** +- `parseNwcUri(uri)` → `{ servicePubkey, relay, secret }` or null. +- `nwcConnect(uri)` — store creds, open WS, send a `get_info` ping to + verify the wallet responds. +- `nwcPayInvoice(bolt11, { timeoutMs, hintMsats })` → resolves to + `{ preimage }` or rejects with `{ code, message }`. +- Credentials live in `localStorage` under + `casewrap-nwc-connection` (single-record; new paste replaces old). + Trade-off acknowledged in the threat-model section. + +**UX (the Settings panel)** +A new "Wallet" section in the Community settings, two-state: + +*Not connected:* +``` +┌────────────────────────────────────────────┐ +│ Lightning wallet — not connected │ +│ │ +│ To unlock premium templates, connect a │ +│ NIP-47 wallet. Recommended: CoinOS │ +│ (free, web-based, no install). │ +│ │ +│ [Set up with CoinOS →] │ +│ [ Paste connection string ▾ ] │ +└────────────────────────────────────────────┘ +``` + +The CoinOS button opens a short modal: +> **1.** Visit [coinos.io](https://coinos.io) and sign up. +> **2.** Open *Settings → Wallet Connect*. +> **3.** Copy the connection string (`nostr+walletconnect://…`). +> **4.** Paste below. +> *[input]* *[Connect]* + +The "Paste connection string" disclosure expands to the same input +with no per-wallet instructions — works for Alby Hub, Mutiny, +Phoenix, Cashu.me, Boltz, etc. + +*Connected:* +``` +┌────────────────────────────────────────────┐ +│ ⚡ Connected to │ +│ Last test: