kind.mx raw

   1  // Package musiquay implements the Musiquay protocol vocabulary: the event
   2  // kinds, tag names, addresses and payload structures that the relay and the
   3  // web app both encode into.
   4  //
   5  // Every structure here is a payload, not an event. A Track is the tag list and
   6  // content of a kind 32210 event; the envelope (id, pubkey, created_at, sig) is
   7  // the nostr layer's job. That split is what lets one vocabulary serve the
   8  // native relay (pkg/event.E) and the wasm client (pkg/core.Event).
   9  //
  10  // The vocabulary comes from the spec in the musiquay repo's docs/:
  11  // FEATURES_MUSIC.md, FEATURES_CREDITS.md, FEATURES_ROSTER.md,
  12  // FEATURES_CALENDAR.md, FEATURES_ACCESS.md, FEATURES_MERCHANDISE.md,
  13  // FEATURES_MARKETPLACE.md, PROTOCOL_FLOWS.md and the kind registry in
  14  // ARCHITECTURE.md. Tag names and value tokens are spelled once, here, as
  15  // constants: the docs are prose and examples, and drift between two
  16  // independent spellings of "mastering-engineer" is a silent data bug.
  17  //
  18  // Where a doc leaves something open, the code says so at the point it matters
  19  // instead of inventing a rule. Unknown tags survive a parse in Extra and are
  20  // re-emitted, so a payload this version does not understand still round-trips.
  21  package protocol
  22  
  23  // Musiquay's own kinds. The addressable block is 32210-32217; 3221, 3222 and
  24  // 32218-32220 are regular (immutable) because they are stored and counted.
  25  const (
  26  	KindTrack          uint16 = 32210
  27  	KindAlbum          uint16 = 32211
  28  	KindPlaylist       uint16 = 32212
  29  	KindSetlist        uint16 = 32213
  30  	KindReleaseCredits uint16 = 32214
  31  	KindTrackCredits   uint16 = 32215
  32  	KindEdition        uint16 = 32216
  33  	KindAccessFilter   uint16 = 32217
  34  
  35  	KindPlayReport      uint16 = 3221
  36  	KindPurchaseOrder   uint16 = 3222
  37  	KindDeliveryReceipt uint16 = 32218
  38  	KindHostAggregate   uint16 = 32219
  39  	KindPublisherRollup uint16 = 32220
  40  )
  41  
  42  // Kinds the vocabulary reuses rather than defining.
  43  const (
  44  	KindCreditLabel uint16 = 1985  // NIP-32 label: confirmation, attestation
  45  	KindComment     uint16 = 1111  // NIP-22 comment: wiki contribution
  46  	KindCalendarDay uint16 = 31922 // NIP-52 date-based event
  47  	KindCalendar    uint16 = 31923 // NIP-52 time-based event, and the roster
  48  	KindCalendarSet uint16 = 31924 // NIP-52 calendar collection
  49  	KindRSVP        uint16 = 31925 // NIP-52 RSVP, and the waitlist entry
  50  	KindStall       uint16 = 30017 // NIP-15 stall
  51  	KindProduct     uint16 = 30018 // NIP-15 product
  52  	KindListing     uint16 = 30402 // NIP-99 active classified listing
  53  	KindListingOff  uint16 = 30403 // NIP-99 draft or closed listing
  54  	KindMusicStatus uint16 = 30315 // NIP-38 live now-playing, d = music
  55  	KindBlobAuth    uint16 = 24242 // BUD-11 Blossom authorization token
  56  	KindBlobServers uint16 = 10063 // NIP-B7 Blossom server list
  57  	KindZapRequest  uint16 = 9734  // NIP-57 zap request, carries the asset a tag
  58  	KindZapReceipt  uint16 = 9735  // NIP-57 zap receipt, the Lightning proof
  59  	KindSeal        uint16 = 13    // NIP-59 seal
  60  	KindGiftWrap    uint16 = 1059  // NIP-59 gift wrap
  61  )
  62  
  63  // KindNames is the registry, in kind order. It is the single source of truth
  64  // for the names used in logs, error messages and relay NIP-11 filtering.
  65  func KindName(k uint16) (s string) {
  66  	switch k {
  67  	case KindTrack:
  68  		return "track"
  69  	case KindAlbum:
  70  		return "album"
  71  	case KindPlaylist:
  72  		return "playlist"
  73  	case KindSetlist:
  74  		return "setlist"
  75  	case KindReleaseCredits:
  76  		return "release-credits"
  77  	case KindTrackCredits:
  78  		return "track-credits"
  79  	case KindEdition:
  80  		return "edition"
  81  	case KindAccessFilter:
  82  		return "access-filter"
  83  	case KindPlayReport:
  84  		return "play-report"
  85  	case KindPurchaseOrder:
  86  		return "purchase-order"
  87  	case KindDeliveryReceipt:
  88  		return "delivery-receipt"
  89  	case KindHostAggregate:
  90  		return "host-aggregate"
  91  	case KindPublisherRollup:
  92  		return "publisher-rollup"
  93  	case KindCreditLabel:
  94  		return "credit-label"
  95  	case KindComment:
  96  		return "comment"
  97  	case KindCalendarDay:
  98  		return "calendar-day"
  99  	case KindCalendar:
 100  		return "calendar"
 101  	case KindCalendarSet:
 102  		return "calendar-set"
 103  	case KindRSVP:
 104  		return "rsvp"
 105  	case KindStall:
 106  		return "stall"
 107  	case KindProduct:
 108  		return "product"
 109  	case KindListing:
 110  		return "listing"
 111  	case KindListingOff:
 112  		return "listing-off"
 113  	case KindMusicStatus:
 114  		return "music-status"
 115  	case KindBlobAuth:
 116  		return "blob-auth"
 117  	case KindBlobServers:
 118  		return "blob-servers"
 119  	case KindZapRequest:
 120  		return "zap-request"
 121  	case KindZapReceipt:
 122  		return "zap-receipt"
 123  	case KindSeal:
 124  		return "seal"
 125  	case KindGiftWrap:
 126  		return "gift-wrap"
 127  	}
 128  	return "unknown"
 129  }
 130  
 131  // IsMusiquay reports whether k is defined by this protocol, as opposed to a
 132  // kind it reuses from a NIP.
 133  func IsMusiquay(k uint16) (ok bool) {
 134  	switch k {
 135  	case KindTrack, KindAlbum, KindPlaylist, KindSetlist,
 136  		KindReleaseCredits, KindTrackCredits, KindEdition, KindAccessFilter,
 137  		KindPlayReport, KindPurchaseOrder, KindDeliveryReceipt,
 138  		KindHostAggregate, KindPublisherRollup:
 139  		return true
 140  	}
 141  	return false
 142  }
 143  
 144  // IsAddressable reports whether a Musiquay kind is parameterized replaceable
 145  // (NIP-01 30000-39999 with a d tag). References to these use a tags: the event
 146  // id changes on every update, the address does not.
 147  func IsAddressable(k uint16) (ok bool) {
 148  	switch k {
 149  	case KindTrack, KindAlbum, KindPlaylist, KindSetlist,
 150  		KindReleaseCredits, KindTrackCredits, KindEdition, KindAccessFilter:
 151  		return true
 152  	}
 153  	return false
 154  }
 155  
 156  // IsRegular reports whether a Musiquay kind is immutable. Play reports, orders,
 157  // receipts and the aggregates are stored and counted, so they can never be
 158  // replaceable: a play count is a count of events.
 159  func IsRegular(k uint16) (ok bool) {
 160  	switch k {
 161  	case KindPlayReport, KindPurchaseOrder, KindDeliveryReceipt,
 162  		KindHostAggregate, KindPublisherRollup:
 163  		return true
 164  	}
 165  	return false
 166  }
 167  
 168  // IsPrivate reports whether a kind is never published to a relay as-is: the
 169  // purchase order and the delivery receipt travel only inside NIP-59 wraps.
 170  func IsPrivate(k uint16) (ok bool) {
 171  	switch k {
 172  	case KindPurchaseOrder, KindDeliveryReceipt:
 173  		return true
 174  	}
 175  	return false
 176  }
 177  
 178  // InReplaceableRange reports whether NIP-01's kind-range rule would treat k as
 179  // parameterized replaceable (30000 <= kind < 40000), whatever the spec intends.
 180  //
 181  // This is a hazard the registry carries: the delivery receipt (32218), host
 182  // aggregate (32219) and publisher rollup (32220) are regular, counted events
 183  // that sit inside the replaceable range. A relay that applies the range rule
 184  // without an exception keeps only one event per (kind, host, empty d) and
 185  // silently drops every other receipt. Storage policy must ask IsRegular first,
 186  // and the spec should move those three out of the range before shipping.
 187  func InReplaceableRange(k uint16) (ok bool) {
 188  	return k >= 30000 && k < 40000
 189  }
 190  
 191  // RegularInReplaceableRange lists the Musiquay kinds that are regular yet sit
 192  // in NIP-01's replaceable range. Storage policy has to special-case exactly
 193  // these; nothing else in the vocabulary collides.
 194  func RegularInReplaceableRange() (ks []uint16) {
 195  	ks = []uint16{:0:3}
 196  	ks = push(ks, KindDeliveryReceipt)
 197  	ks = push(ks, KindHostAggregate)
 198  	ks = push(ks, KindPublisherRollup)
 199  	return
 200  }
 201