# BUD-07 Implementation Lab > Reference implementation of Cashu micropayments for Blossom blob storage. ## Overview BUD-07 enables content monetization on Blossom servers using Cashu ecash tokens. Artists upload media with payment metadata, and listeners pay with Cashu tokens to access content. **Repository:** [github.com/musiquay/musiquay](https://github.com/musiquay/musiquay) (lab/ directory) ## Components | Component | Description | |-----------|-------------| | **Bitcoin Core** | Private regtest chain for testing | | **LND** | Two Lightning nodes for payment channel | | **Gonuts** | Cashu mint (forked for LND 0.21+ compatibility) | | **Orly** | Nostr relay with BUD-07 Blossom payment gating | ## Payment Modes ### Required Payment (MinSats > 0) Artist specifies minimum payment. Access blocked without valid token. ```bash # Upload with payment requirement curl -X PUT http://localhost:10547/upload \ -H "X-Artist-Pubkey: npub1..." \ -H "X-Min-Sats: 100" \ -H "X-Mint-Url: http://mint.example.com" \ --data-binary @song.mp3 # Download without token → 402 Payment Required curl http://localhost:10547/blossom/ # Download with valid Cashu token → 200 OK curl -H "X-Cashu: cashuB..." http://localhost:10547/blossom/ ``` ### Free with Optional Payment (MinSats = 0) Content is freely accessible, but voluntary payments are accepted and forwarded to the artist. ```bash # Upload as free content curl -X PUT http://localhost:10547/upload \ -H "X-Artist-Pubkey: npub1..." \ -H "X-Min-Sats: 0" \ -H "X-Mint-Url: http://mint.example.com" \ --data-binary @song.mp3 # Download without token → 200 OK (free access) curl http://localhost:10547/blossom/ # Download with voluntary tip → 200 OK (token forwarded to artist) curl -H "X-Cashu: cashuB..." http://localhost:10547/blossom/ ``` ## Payment Flow ``` ┌──────────┐ 1. Mint tokens ┌──────────┐ │ Listener │ ──────────────────────▶│ Mint │ │ (Wallet) │ ◀───────────────────── │ (Cashu) │ └────┬─────┘ Lightning invoice └──────────┘ │ │ 2. Request blob with X-Cashu token ▼ ┌──────────┐ 3. Validate token ┌──────────┐ │ Blossom │ ───────────────────────▶│ Mint │ │ Server │ ◀────────────────────── │ │ └────┬─────┘ Swap/burn proofs └──────────┘ │ │ 4. Return content ▼ ┌──────────┐ │ Listener │ └──────────┘ ``` ## Security Features - **Replay protection:** Tokens are hashed and tracked; reuse returns 400 - **HTTPS required:** Production mints must use HTTPS (SSRF protection) - **Localhost exception:** HTTP allowed for 127.0.0.1/localhost/[::1] in development ## Minimum Payment Unit The minimum payment is **1 satoshi**: - Lightning Network minimum invoice: 1 sat - Cashu tokens require cryptographic proofs (zero-value tokens have empty proofs) ## Quick Start ```bash # Clone the lab git clone git@github.com:musiquay/musiquay.git cd musiquay/lab git submodule update --init # Enter devbox and build devbox shell just build-all # Start everything just start-all # Run BUD-07 tests just test-bud07 ``` ## Clean Slate Timing From empty environment to working stack: - Clone + devbox init: ~12s - Build binaries: ~130s - Bitcoin + LND + channel: ~25s - Mint + Orly: ~10s - **Total: ~3 minutes** ## Future Work ### Nostr Play Events Play reports (kind 3221) publish auditable play counts to the Nostr network - full spec in [FEATURES_MUSIC.md](./FEATURES_MUSIC.md): - Tags: track address (`a`), artist pubkey (`p`), blob hash (`x`), blossom server, codec - Paid plays carry a host BDHKE blind signature (`blind`, `host`, `statement` tags) certifying the play without identifying the listener; free plays are self-attested - The live "now playing" social layer is separate: NIP-38 kind 30315 `d:music` status events - expiring, addressable, already defined. Play reports are the record; NIP-38 statuses are the live feed. ## Prototype Delta The current orly BUD-07 implementation (pay-per-request token gating) predates the access model in [FEATURES_ACCESS.md](./FEATURES_ACCESS.md). The gap, in order of priority: 1. **Artist payout is broken.** `swapAtMint` discards the swap response and blinding scalars, so the listener's proofs are burned and the new proofs are unclaimable; the artist is never paid. Must be fixed before any sale is real. 2. **Entitlement replaces per-request payment.** Paid downloads should check access filter membership (kind 32217) instead of demanding a fresh `X-Cashu` token per request. Re-downloads must be free for entitled buyers. 3. **Delivery receipts.** The host should issue kind 32218 receipts (NIP-59 wrapped, to buyer and publisher) on every serve, and publish host aggregates (32219) per period. 4. **Blind signatures.** The host should blind-sign paid play statements with BDHKE so listeners can publish certified, unlinkable play reports. 5. **NUT-24 compliance.** The 402 `X-Cashu` challenge is currently bespoke JSON; it should be a NUT-24 payment request, which requires NUT-18/NUT-26 `creqA`/`creqB` encoding. NUT-24 stays for rental stream buckets; ownership purchases move to the kind 3222 order in [FEATURES_ACCESS.md](./FEATURES_ACCESS.md). Note that the `gonuts` wallet does not implement NUT-18/26 today, so a client cannot yet complete the `402` handshake without adding it. 6. **Replay state.** The in-memory `seenTokens` map is never pruned and not persisted; the mint is the real replay backstop, but the local map should be bounded. 7. **Free-with-tip mode.** Upload never writes a `MinSats = 0` marker, so the documented optional-tip path is unreachable. The spec now defines tips explicitly: a tip is a payment leg recorded on the delivery receipt (`amount_sats` plus token hashes in `paid`), and it never creates or changes a filter entry — an asset is free if and only if no filter event exists for it. Implement the marker so the path is reachable. ## Related Documentation - [ARCHITECTURE.md](./ARCHITECTURE.md) - Platform architecture overview - [BUSINESS_MODEL.md](./BUSINESS_MODEL.md) - Direct compensation model - [FEATURES_ACCESS.md](./FEATURES_ACCESS.md) - Paid access, receipts, aggregates, retailer model - [FEATURES_MUSIC.md](./FEATURES_MUSIC.md) - Tracks, albums, playlists, play reports