types.go raw
1 package relayinfo
2
3 import (
4 "encoding/json"
5 "errors"
6 "os"
7 "sort"
8 "sync"
9
10 "next.orly.dev/pkg/nostr/encoders/kind"
11 "next.orly.dev/pkg/nostr/encoders/timestamp"
12 "next.orly.dev/pkg/nostr/utils/number"
13 "next.orly.dev/pkg/lol/chk"
14 "next.orly.dev/pkg/lol/log"
15 )
16
17 // NIP is a number and description of a nostr "improvement" possibility.
18 type NIP struct {
19 Description string
20 Number int
21 }
22
23 // N returns the number of a nostr "improvement" possibility.
24 func (n NIP) N() int { return n.Number }
25
26 // GetList converts a NIP into a number.List of simple numbers, sorted in
27 // ascending order.
28 func GetList(items ...NIP) (n number.List) {
29 for _, item := range items {
30 n = append(n, item.N())
31 }
32 sort.Sort(n)
33 return
34 }
35
36 // this is the list of all nips and their titles for use in the supported_nips
37 // field
38 var (
39 BasicProtocol = NIP{"Basic protocol flow description", 1}
40 NIP1 = BasicProtocol
41 FollowList = NIP{"Follow List", 2}
42 NIP2 = FollowList
43 OpenTimestampsAttestations = NIP{
44 "OpenTimestamps Attestations for Events", 3,
45 }
46 NIP3 = OpenTimestampsAttestations
47 EncryptedDirectMessage = NIP{
48 "Encrypted Direct Message -- unrecommended: deprecated in favor of NIP-17",
49 4,
50 }
51 NIP4 = EncryptedDirectMessage
52 MappingNostrKeysToDNS = NIP{
53 "Mapping Nostr keys to DNS-based internet identifiers", 5,
54 }
55 NIP5 = MappingNostrKeysToDNS
56 BasicKeyDerivation = NIP{
57 "Basic key derivation from mnemonic seed phrase", 6,
58 }
59 NIP6 = BasicKeyDerivation
60 WindowNostrCapability = NIP{
61 "window.nostr capability for web browsers", 7,
62 }
63 NIP7 = WindowNostrCapability
64 HandlingMentions = NIP{
65 "Handling Mentions -- unrecommended: deprecated in favor of NIP-27", 8,
66 }
67 NIP8 = HandlingMentions
68 EventDeletion = NIP{"Event Deletion Request", 9}
69 NIP9 = EventDeletion
70 TextNotesAndThreads = NIP{"Text Notes and Threads", 10}
71 NIP10 = TextNotesAndThreads
72 RelayInformationDocument = NIP{"Relay Information Document", 11}
73 NIP11 = RelayInformationDocument
74 GenericTagQueries = NIP{"Generic Tag Queries", 12}
75 NIP12 = GenericTagQueries
76 ProofOfWork = NIP{"Proof of Work", 13}
77 NIP13 = ProofOfWork
78 SubjectTag = NIP{"Subject tag in text events", 14}
79 NIP14 = SubjectTag
80 NostrMarketplace = NIP{
81 "Nostr Marketplace (for resilient marketplaces)", 15,
82 }
83 NIP15 = NostrMarketplace
84 EventTreatment = NIP{"Event Treatment", 16}
85 NIP16 = EventTreatment
86 PrivateDirectMessages = NIP{"Private Direct Messages", 17}
87 NIP17 = PrivateDirectMessages
88 Reposts = NIP{"Reposts", 18}
89 NIP18 = Reposts
90 Bech32EncodedEntities = NIP{"bech32-encoded entities", 19}
91 NIP19 = Bech32EncodedEntities
92 CommandResults = NIP{"Command Results", 20}
93 NIP20 = CommandResults
94 NostrURIScheme = NIP{"nostr: URI scheme", 21}
95 NIP21 = NostrURIScheme
96 Comment = NIP{"Comment", 22}
97 NIP22 = Comment
98 LongFormContent = NIP{"Long-form Content", 23}
99 NIP23 = LongFormContent
100 ExtraMetadata = NIP{"Extra metadata fields and tags", 24}
101 NIP24 = ExtraMetadata
102 Reactions = NIP{"Reactions", 25}
103 NIP25 = Reactions
104 DelegatedEventSigning = NIP{
105 "Delegated Event Signing -- unrecommended: adds unnecessary burden for little gain",
106 26,
107 }
108 NIP26 = DelegatedEventSigning
109 TextNoteReferences = NIP{"Text Note References", 27}
110 NIP27 = TextNoteReferences
111 PublicChat = NIP{"Public Chat", 28}
112 NIP28 = PublicChat
113 RelayBasedGroups = NIP{"Relay-based Groups", 29}
114 NIP29 = RelayBasedGroups
115 CustomEmoji = NIP{"Custom Emoji", 30}
116 NIP30 = CustomEmoji
117 DealingWithUnknownEvents = NIP{"Dealing with Unknown Events", 31}
118 NIP31 = DealingWithUnknownEvents
119 Labeling = NIP{"Labeling", 32}
120 NIP32 = Labeling
121 ParameterizedReplaceableEvents = NIP{"Parameterized Replaceable Events", 33}
122 NIP33 = ParameterizedReplaceableEvents
123 GitStuff = NIP{"git stuff", 34}
124 NIP34 = GitStuff
125 Torrents = NIP{"Torrents", 35}
126 NIP35 = Torrents
127 SensitiveContent = NIP{"Sensitive Content", 36}
128 NIP36 = SensitiveContent
129 DraftEvents = NIP{"Draft Events", 37}
130 NIP37 = DraftEvents
131 UserStatuses = NIP{"User Statuses", 38}
132 NIP38 = UserStatuses
133 ExternalIdentitiesInProfiles = NIP{"External Identities in Profiles", 39}
134 NIP39 = ExternalIdentitiesInProfiles
135 ExpirationTimestamp = NIP{"Expiration Timestamp", 40}
136 NIP40 = ExpirationTimestamp
137 Authentication = NIP{
138 "Authentication of clients to relays", 42,
139 }
140 NIP42 = Authentication
141 RelayAccessMetadata = NIP{
142 "Relay Access Metadata and Requests", 43,
143 }
144 NIP43 = RelayAccessMetadata
145 VersionedEncryption = NIP{"Encrypted Payloads (Versioned)", 44}
146 NIP44 = VersionedEncryption
147 CountingResults = NIP{"Counting results", 45}
148 NIP45 = CountingResults
149 NostrRemoteSigning = NIP{"Nostr Remote Signing", 46}
150 NIP46 = NostrRemoteSigning
151 WalletConnect = NIP{"Nostr Wallet Connect", 47}
152 NIP47 = WalletConnect
153 ProxyTags = NIP{"Proxy Tags", 48}
154 NIP48 = ProxyTags
155 PrivateKeyEncryption = NIP{"Private Key Encryption", 49}
156 NIP49 = PrivateKeyEncryption
157 SearchCapability = NIP{"Search Capability", 50}
158 NIP50 = SearchCapability
159 Lists = NIP{"Lists", 51}
160 NIP51 = Lists
161 CalendarEvents = NIP{"Calendar Events", 52}
162 NIP52 = CalendarEvents
163 LiveActivities = NIP{"Live Activities", 53}
164 NIP53 = LiveActivities
165 Wiki = NIP{"Wiki", 54}
166 NIP54 = Wiki
167 AndroidSignerApplication = NIP{"Android Signer Application", 55}
168 NIP55 = AndroidSignerApplication
169 Reporting = NIP{"Reporting", 56}
170 NIP56 = Reporting
171 LightningZaps = NIP{"Lightning Zaps", 57}
172 NIP57 = LightningZaps
173 Badges = NIP{"Badges", 58}
174 NIP58 = Badges
175 GiftWrap = NIP{"Gift Wrap", 59}
176 NIP59 = GiftWrap
177 CashuWallet = NIP{"Cashu Wallet", 60}
178 NIP60 = CashuWallet
179 Nutzaps = NIP{"Nutzaps", 61}
180 NIP61 = Nutzaps
181 RequestToVanish = NIP{"Request to Vanish", 62}
182 NIP62 = RequestToVanish
183 ChessPGN = NIP{"Chess (PGN)", 64}
184 NIP64 = ChessPGN
185 RelayListMetadata = NIP{"Relay List Metadata", 65}
186 NIP65 = RelayListMetadata
187 RelayDiscoveryAndLiveness = NIP{
188 "Relay Discovery and Liveness Monitoring", 66,
189 }
190 NIP66 = RelayDiscoveryAndLiveness
191 PictureFirstFeeds = NIP{"Picture-first feeds", 68}
192 NIP68 = PictureFirstFeeds
193 PeerToPeerOrderEvents = NIP{"Peer-to-peer Order events", 69}
194 NIP69 = PeerToPeerOrderEvents
195 ProtectedEvents = NIP{"Protected Events", 70}
196 NIP70 = ProtectedEvents
197 VideoEvents = NIP{"Video Events", 71}
198 NIP71 = VideoEvents
199 ModeratedCommunities = NIP{"Moderated Communities", 72}
200 NIP72 = ModeratedCommunities
201 ExternalContentIDs = NIP{"External Content IDs", 73}
202 NIP73 = ExternalContentIDs
203 ZapGoals = NIP{"Zap Goals", 75}
204 NIP75 = ZapGoals
205 NegentropySync = NIP{"Negentropy Syncing", 77}
206 NIP77 = NegentropySync
207 ApplicationSpecificData = NIP{"Application-specific data", 78}
208 NIP78 = ApplicationSpecificData
209 Threads = NIP{"Threads", 0x7D}
210 NIP7D = Threads
211 Highlights = NIP{"Highlights", 84}
212 NIP84 = Highlights
213 RelayManagementAPI = NIP{"Relay Management API", 86}
214 NIP86 = RelayManagementAPI
215 EcashMintDiscoverability = NIP{"Ecash Mint Discoverability", 87}
216 NIP87 = EcashMintDiscoverability
217 Polls = NIP{"Polls", 88}
218 NIP88 = Polls
219 RecommendedApplicationHandlers = NIP{"Recommended Application Handlers", 89}
220 NIP89 = RecommendedApplicationHandlers
221 DataVendingMachines = NIP{"Data Vending Machines", 90}
222 NIP90 = DataVendingMachines
223 MediaAttachments = NIP{"Media Attachments", 92}
224 NIP92 = MediaAttachments
225 FileMetadata = NIP{"File Metadata", 94}
226 NIP94 = FileMetadata
227 HTTPFileStorageIntegration = NIP{
228 "HTTP File Storage Integration -- unrecommended: replaced by blossom APIs",
229 96,
230 }
231 NIP96 = HTTPFileStorageIntegration
232 HTTPAuth = NIP{"HTTP Auth", 98}
233 NIP98 = HTTPAuth
234 ClassifiedListings = NIP{"Classified Listings", 99}
235 NIP99 = ClassifiedListings
236 VoiceMessages = NIP{"Voice Messages", 0xA0}
237 NIPA0 = VoiceMessages
238 WebBookmarks = NIP{"Web Bookmarks", 0xB0}
239 NIPB0 = WebBookmarks
240 Blossom = NIP{"Blossom", 0xB7}
241 NIPB7 = Blossom
242 CodeSnippets = NIP{"Code Snippets", 0xC0}
243 NIPC0 = CodeSnippets
244 Chats = NIP{"Chats", 0xC7}
245 NIPC7 = Chats
246 E2EEMessagingUsingMLSProtocol = NIP{
247 "E2EE Messaging using MLS Protocol", 0xEE,
248 }
249 NIPEE = E2EEMessagingUsingMLSProtocol
250 )
251
252 var NIPMap = map[int]NIP{
253 1: NIP1, 2: NIP2, 3: NIP3, 4: NIP4, 5: NIP5, 6: NIP6, 7: NIP7, 8: NIP8,
254 9: NIP9, 10: NIP10,
255 11: NIP11, 12: NIP12, 13: NIP13, 14: NIP14, 15: NIP15, 16: NIP16, 17: NIP17,
256 18: NIP18, 19: NIP19, 20: NIP20,
257 21: NIP21, 22: NIP22, 23: NIP23, 24: NIP24, 25: NIP25, 26: NIP26, 27: NIP27,
258 28: NIP28, 29: NIP29, 30: NIP30,
259 31: NIP31, 32: NIP32, 33: NIP33, 34: NIP34, 35: NIP35, 36: NIP36, 37: NIP37,
260 38: NIP38, 39: NIP39, 40: NIP40,
261 42: NIP42, 44: NIP44, 45: NIP45, 46: NIP46, 47: NIP47, 48: NIP48, 49: NIP49,
262 50: NIP50,
263 51: NIP51, 52: NIP52, 53: NIP53, 54: NIP54, 55: NIP55, 56: NIP56, 57: NIP57,
264 58: NIP58, 59: NIP59, 60: NIP60,
265 61: NIP61, 62: NIP62, 64: NIP64, 65: NIP65, 66: NIP66, 68: NIP68, 69: NIP69,
266 70: NIP70,
267 71: NIP71, 72: NIP72, 73: NIP73, 75: NIP75, 77: NIP77, 78: NIP78,
268 0x7D: NIP7D, 84: NIP84,
269 86: NIP86, 87: NIP87, 88: NIP88, 89: NIP89, 90: NIP90, 92: NIP92, 94: NIP94,
270 96: NIP96, 98: NIP98, 99: NIP99,
271 0xA0: NIPA0, 0xB0: NIPB0, 0xB7: NIPB7, 0xC0: NIPC0, 0xC7: NIPC7,
272 0xEE: NIPEE,
273 }
274
275 // Limits are rules about what is acceptable for events and filters on a relay.
276 type Limits struct {
277 // MaxMessageLength is the maximum number of bytes for incoming JSON that
278 // the relay will attempt to decode and act upon. When you send large
279 // subscriptions, you will be limited by this value. It also effectively
280 // limits the maximum size of any event. Value is calculated from [ to ] and
281 // is after UTF-8 serialization (so some Unicode characters will cost 2-3
282 // bytes). It is equal to the maximum size of the WebSocket message frame.
283 MaxMessageLength int `json:"max_message_length,omitempty"`
284 // MaxSubscriptions is the total number of subscriptions that may be active
285 // on a single websocket connection to this relay. It's possible that
286 // authenticated clients with a (paid) relationship to the relay may have
287 // higher limits.
288 MaxSubscriptions int `json:"max_subscriptions,omitempty"`
289 // MaxFilter is the maximum number of filter values in each subscription.
290 // Must be one or higher.
291 MaxFilters int `json:"max_filters,omitempty"`
292 // MaxLimit is the relay server will clamp each filter's limit value to this
293 // number. This means the client won't be able to get more than this number
294 // of events from a single subscription filter. This clamping is typically
295 // done silently by the relay, but with this number, you can know that there
296 // are additional results if you narrowed your filter's time range or other
297 // parameters.
298 MaxLimit int `json:"max_limit,omitempty"`
299 // MaxSubidLength is the maximum length of subscription id as a string.
300 MaxSubidLength int `json:"max_subid_length,omitempty"`
301 // MaxEventTags in any event, this is the maximum number of elements in the
302 // tags list.
303 MaxEventTags int `json:"max_event_tags,omitempty"`
304 // MaxContentLength maximum number of characters in the content field of any
305 // event. This is a count of Unicode characters. After serializing into JSON
306 // it may be larger (in bytes), and is still subject to the
307 // max_message_length, if defined.
308 MaxContentLength int `json:"max_content_length,omitempty"`
309 // MinPowDifficulty new events will require at least this difficulty of PoW,
310 // based on NIP-13, or they will be rejected by this server.
311 MinPowDifficulty int `json:"min_pow_difficulty,omitempty"`
312 // AuthRequired means the relay requires NIP-42 authentication to happen
313 // before a new connection may perform any other action. Even if set to
314 // False, authentication may be required for specific actions.
315 AuthRequired bool `json:"auth_required"`
316 // PaymentRequired this relay requires payment before a new connection may
317 // perform any action.
318 PaymentRequired bool `json:"payment_required"`
319 // RestrictedWrites means this relay requires some kind of condition to be
320 // fulfilled to accept events (not necessarily, but including
321 // payment_required and min_pow_difficulty). This should only be set to true
322 // when users are expected to know the relay policy before trying to write
323 // to it -- like belonging to a special pubkey-based whitelist or writing
324 // only events of a specific niche kind or content. Normal anti-spam
325 // heuristics, for example, do not qualify.q
326 RestrictedWrites bool `json:"restricted_writes"`
327 Oldest *timestamp.T `json:"created_at_lower_limit,omitempty"`
328 Newest *timestamp.T `json:"created_at_upper_limit,omitempty"`
329 }
330
331 // Payment is an amount and currency unit name.
332 type Payment struct {
333 Amount int `json:"amount"`
334 Unit string `json:"unit"`
335 }
336
337 // Sub is a subscription, with the Payment and the period it yields.
338 type Sub struct {
339 Payment
340 Period int `json:"period"`
341 }
342
343 // Pub is a limitation for what you can store on the relay as a kinds.S and the
344 // cost (for???).
345 type Pub struct {
346 Kinds kind.S `json:"kinds"`
347 Payment
348 }
349
350 // T is the relay information document.
351 type T struct {
352 Name string `json:"name"`
353 Description string `json:"description,omitempty"`
354 PubKey string `json:"pubkey,omitempty"`
355 Contact string `json:"contact,omitempty"`
356 Nips number.List `json:"supported_nips"`
357 Software string `json:"software"`
358 Version string `json:"version"`
359 Limitation Limits `json:"limitation,omitempty"`
360 Retention any `json:"retention,omitempty"`
361 RelayCountries []string `json:"relay_countries,omitempty"`
362 LanguageTags []string `json:"language_tags,omitempty"`
363 Tags []string `json:"tags,omitempty"`
364 PostingPolicy string `json:"posting_policy,omitempty"`
365 PaymentsURL string `json:"payments_url,omitempty"`
366 Fees *Fees `json:"fees,omitempty"`
367 Icon string `json:"icon"`
368 sync.Mutex
369 }
370
371 // NewInfo populates the nips map, and if an Info structure is provided, it is
372 // used and its nips map is populated if it isn't already.
373 func NewInfo(inf *T) (info *T) {
374 if inf != nil {
375 info = inf
376 } else {
377 info = &T{
378 Limitation: Limits{
379 MaxLimit: 500,
380 },
381 }
382 }
383 return
384 }
385
386 // Clone replicates a relayinfo.T.
387 // todo: this could be done better, but i don't think it's in use.
388 func (ri *T) Clone() (r2 *T, err error) {
389 r2 = new(T)
390 var b []byte
391 // beware, this will escape <, > and & to unicode escapes but that should be
392 // ok since this data is not signed in events until after it is marshaled.
393 if b, err = json.Marshal(ri); chk.E(err) {
394 return
395 }
396 if err = json.Unmarshal(b, r2); chk.E(err) {
397 return
398 }
399 return
400 }
401
402 // AddNIPs adds one or more numbers to the list of NIPs.
403 func (ri *T) AddNIPs(n ...int) {
404 ri.Lock()
405 for _, num := range n {
406 ri.Nips = append(ri.Nips, num)
407 }
408 ri.Unlock()
409 }
410
411 // HasNIP returns true if the given number is found in the list.
412 func (ri *T) HasNIP(n int) (ok bool) {
413 ri.Lock()
414 for i := range ri.Nips {
415 if ri.Nips[i] == n {
416 ok = true
417 break
418 }
419 }
420 ri.Unlock()
421 return
422 }
423
424 // Save the relayinfo.T to a given file as JSON.
425 func (ri *T) Save(filename string) (err error) {
426 if ri == nil {
427 err = errors.New("cannot save nil relay info document")
428 log.E.Ln(err)
429 return
430 }
431 var b []byte
432 // beware, this will escape <, > and & to unicode escapes but that should be
433 // ok since this data is not signed in events until after it is marshaled.
434 if b, err = json.MarshalIndent(ri, "", " "); chk.E(err) {
435 return
436 }
437 if err = os.WriteFile(filename, b, 0600); chk.E(err) {
438 return
439 }
440 return
441 }
442
443 // Load a given file and decode the JSON relayinfo.T encoded in it.
444 func (ri *T) Load(filename string) (err error) {
445 if ri == nil {
446 err = errors.New("cannot load into nil config")
447 log.E.Ln(err)
448 return
449 }
450 var b []byte
451 if b, err = os.ReadFile(filename); chk.E(err) {
452 return
453 }
454 // log.S.ToSliceOfBytes("realy information document\n%s", string(b))
455 if err = json.Unmarshal(b, ri); chk.E(err) {
456 return
457 }
458 return
459 }
460