In live testing, the tag-only gate shipped in Phases 0–3 turned out
to be too leaky: the design payload was published in plaintext, three
separate import paths (feed-card Use/Fork, preview-pane Use/Fork,
preview-pane Save JSON) bypassed the lock, and any non-Artstr client
could ignore the tag entirely. Adam pulled back the original detailed
spec he wrote earlier — the static soft-gate + buyer purchase vault
model — which solves the protection gap WITHOUT requiring a creator-
online bot (the route I was about to build).
Key idea: the AES key derives deterministically from
HKDF-SHA256(softGatePepperForEpoch(epoch), publicDesignSalt, info =
"artstr-softgate-v1" + epoch + creatorPubkey + premiumAddress +
schemaVersion). The pepper lives in the static client (obfuscated,
not secret). The client refuses to derive the key until valid kind-
9735 receipts confirm the split zap. Same key for every buyer; no
creator-online requirement; cross-device unlock via a NIP-44 self-
encrypted purchase vault (kind-30078, d=artstr:purchase-vault:v1).
Files:
- docs/PREMIUM_DESIGNS_FEATURE.md (new, canonical) — the full
soft-gate + split-zap + purchase-vault spec. Sections 1–17 are
the original spec verbatim. Section 18 maps the shipped Phases
0–3 to the spec's implementation-plan items (10.0–10.6) so the
next contributor sees what's done. Section 19 lists the
remaining work in build order: soft-gate crypto primitive →
watermarked preview → premium publish event → premium consume
→ receipt fast-path by address → purchase vault → My Purchases
UI → hardening + copy.
- docs/ZAP_GATED_PREMIUM_FEATURE.md — banner now points at
PREMIUM_DESIGNS_FEATURE.md as the canonical going-forward doc.
The obsolete Phases 5/6 (DM-delivery key flow) I sketched
earlier today are removed; the "Content model (CORRECTED)"
section is trimmed and points at the new spec. The file is
kept for Phases 0–3 history, the NWC/tip integration notes,
and the threat model that still applies.
- docs/ZAP_GATED_TEMPLATES_FEATURE.md and ZAPS_NWC_FEATURE.md
keep their "superseded" banners pointing at
ZAP_GATED_PREMIUM_FEATURE.md (which itself now points at
PREMIUM_DESIGNS_FEATURE.md) — chain of provenance preserved.
- TODO.md: drafted-but-unbuilt entry rewritten to describe the
final shape (encrypted + vault + honest soft-gate framing) and
point at the new spec; references the older docs for the
shipped foundation.
No code changes yet — the spec adoption is the gate. Next commits
implement the soft-gate crypto primitive, the watermarked preview,
and the premium publish path.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Critical additions:
1. Relay fast-path. Zap receipts live on relays forever — querying
for matching receipts on feed load means a cache-wiped user (or
a new device) restores their unlocks for free. Also auto-detects
a half-failed unlock that eventually settled.
2. NIP-47 capability discovery via kind-13194. We subscribe to the
wallet's info event on connect and verify pay_invoice is
supported before allowing the user to proceed.
3. Wallet-budget-exhausted handling. Special-case QUOTA_EXCEEDED /
BUDGET_EXCEEDED so we say "raise your wallet's NWC budget" not
a generic failure. Also surface this guidance in the CoinOS
setup flow itself ("set a budget that comfortably covers the
unlocks you expect").
4. Preimage = proof of payment. Decouple unlock UX from kind-9735
receipt arrival. The preimage hashes to the bolt11's payment
hash; that's all we need to mark unlocked locally. Receipts
become async verification.
5. nwcLookupInvoice for double-spend protection. Before paying any
leg on retry, check whether the wallet already settled it.
Combined with PARTIAL_UNLOCK_KEY persistence (now storing bolt11
+ paymentHash, not just receipt id), this means cross-session
retries never double-charge.
6. LNURL minSendable checks. Block at publish time if the creator's
LNURL won't accept their split share (with a corrective hint
showing the minimum viable zap-min). Same defensive check on
the platform side.
7. Parallelize the dual-zap. Sign both zap requests in Promise.all,
fetch both bolt11s in parallel, then pay sequentially with
creator first. Roughly halves the user-facing latency.
8. Partial-unlock persistence + banner. If the user closes the tab
mid-unlock, on next visit a top-bar banner offers Resume or
Discard for unfinished unlocks.
Polish:
- Phase 0 bumped 2-3h → 3-5h (noble subset inlining is genuinely
more work than that estimate).
- NWC URI parser now handles multiple relay params, URL-encoded
secrets, optional lud16 param.
- "Wallet alias from get_info" instead of misleading "walletDomain".
- CoinOS deep link to coinos.io/settings/connect.
- Mobile copy-paste note.
- Three new threat-model rows: platform-pubkey exposure (intentional),
budget-exhausted-mid-flow (precise retry prompt), and forged-
receipt fast-path (signature verification).
- Two new open-questions: pay-above-min unlocks (creator-only
bonus), currency display.
- Phase 4 ship gate: "documented in commit message" since we push
straight to main, no PR workflow.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
The original ZAP_GATED_TEMPLATES_FEATURE.md said "no platform fee, no
backend" — that's exactly the opposite of what we actually want to
ship. ZAPS_NWC_FEATURE.md was scoped as a general tip upgrade,
listing splits as a "future enhancement." For our purposes the two
are a single arc, with NWC being the only thing that gives the
mandatory creator/platform split a tolerable UX.
ZAP_GATED_PREMIUM_FEATURE.md is the new canonical plan:
- 5 phases (0 crypto bundle, 1 NWC, 2 platform-fee plumbing,
3 zap-gated UI, 4 E2E + edge cases). ~16-23h total.
- 30% platform fee, single-constant for easy adjustment later.
- PLATFORM_NPUB hard-coded; lud16 resolved dynamically from the
npub's kind-0 each session (so the lightning address can change
later without a code update).
- CoinOS recommended via a guided "Set up with CoinOS" path in
the wallet-connection modal; generic "paste any NIP-47 URI"
fallback for Alby Hub, Mutiny, Phoenix, Cashu.me, etc.
- Idempotent retry of partial unlocks via localStorage receipt
tracking — creator-leg pays first so a half-failed unlock at
least pays the creator.
- Explicit threat model + non-goals (no creator-only fallback,
no refunds, no bolt11-QR fallback path).
The two old specs are marked superseded with a banner at the top so
historical context is preserved without future contributors getting
crossed wires. TODO.md drops them from the drafted-but-unbuilt list
and points to the merged doc; it also fixes the stale Private
Publishing entry — Phases 1 & 2 already shipped in 3e6e89e, only
Phases 3 & 4 remain.
Co-Authored-By: Claude Opus 4.7 <[email protected]>