Oddbean new post about | logout

Notes by Cody | export

 Quoting this for myself so that I remember to go add my tool once it’s in a state fit for public consumption.

(For anyone curious, it’s a little self-contained executable that can let you browse and, eventually, post to, a single relay. Basically me making a web-based, SSR Nostr client that works just like I want.)
nostr:note1h5a5uswq2crnmezme6zqzqa9dxdj86ye70ty4tntr0gc9832r8lskuk6jp 
 Idea: a #nostr event kind for leaving a comment on a web page.

Web pages could embed some metadata[1] to say “discuss this on Nostr” and plugins could query known/approved relays for comments.

Could include a “selection” metadata to reply to a particular part of the content.

#nostrdev 
 [1]: Why include metadata? Well, the alternative would be for Nostr to constantly be sending your web history to relays as you browse the majority of sites without any comments on them. Metadata lets you opt in. And could let sites control (w/ optional client support) which comments/relays appear by default. Would probably be good to have it specify a canonical URL so all discussions end up w/ the same tag, too.  
 Trump found guilty on 34/34 charges of falsifying business records re: paying hush money to Stormy Daniels. 🎉 

https://www.npr.org/2024/05/30/nx-s1-4977352/trump-trial-verdict

Now, when is sentencing? ⏳  
 What tests would you want to see in a relay test suite? 
 Not exactly sure how to handle it but … rate limiting that doesn’t break the spec?

Ex: IIRC, nos.lol would “rate limit” my queries by
1) sending a NOTICE to “slow down”. (Notably absent, any subscription ID.)
2) NOT sending an EOSE, CLOSE, or any events for the query that caused triggered the limit.

… Which can make a naive client hang forever waiting for messages that are never coming.  
 LOL. Old enough to remember when avoiding centralized bank transaction fees was one of the big benefits touted by bitcoiners.

[insert Pepperidge Farm Remembers meme here]
nostr:note10uv7uwynw24f55wy3yf2d70nrgce0g6v99davq5gc54khc2fgstqvfjhuw 
 Mutation is just inherently more complicated than storing immutable events. If someone is storing a new immutable event every 100ms they’re easy to spot as a potential spammer/abuse.

If they’re replacing an event every 100ms and a server naively just deletes old versions, now you don’t have a record of how much they’re (ab)using your resources.

Plus, a big benefit of signed events is holding people (or at least, pubkeys) accountable for what they’ve posted. If they can just edit it out of history, that is lost.

For that reason, my FeoBlog system disallowed mutability. But, that was super frustrating when I would inevitably discover a typo in what I just posted. So, while it’s more complicated, I’m glad that Nostr has a solution there.

But in my Nostr relay implementation, I keep older versions of mutable events so that you can see previous versions that a user posted. I’m keeping the receipts. 😉  
 One solution to this is to only read replies from "safe" relays.

We don't have enough safe relay... 
 That’s one of the big reasons I started writing my own relay: 
https://github.com/nfnitloop/yastr?tab=readme-ov-file#yastr

If you make a distributed system that’s open to everyone all the time, you’ll get inundated with spam/trolling/bots/abuse. IMO a better solution is to let it be open to read, but restricted to write. People can quote and reply to content but it doesn’t mean I have to see it if I don’t want to.  
 It works really well for files under 100KB. We are waiting for new Relay codebases to better mana... 
 I’ve got an experimental implementation of multipart nip-95 files working. Hope to cleans it up and have it on my server for folks to check out this week.  
 It’s not yet super optimized. Making sure the rules around accepting such messages and reassembling them are working well first.

Optimization of the implementation itself can come in a later iteration, without changing the public interface.

Plus, any implementation details there will probably be specific to the relay implementation. Happy to optimize mine to be an example to follow, but I don’t imagine it’ll just be a copy/paste to add it to other relays.

The ideas I have for optimization are basically: deconstruct the kind 1064 JSON messages so you can store the blob as binary instead of base64. (This is done transparently on the server. It still has to reconstruct and serve text JSON events when they’re requested, since Nostr protocol requires it.)

You can do that at the event level and save some space. And it’s the simpler implementation. But, if you expect people might re-post the same content, an implementation that can reassemble the full file and store that in a content-addressable store would be even better. But that gets more complicated with multipart files. (Can’t decode the whole blob and verify and store it until all messages are present.) 
 Alright, #nostr / #nostrdev folks, I've got my implementation of multipart NIP-95 files up here:

https://github.com/NfNitLoop/yastr/blob/main/src/server/nip95.rs

(Yes, it's very messy. I just saw all the TODOs which I've already done. 😆)

And I've got a little tool to upload multi-part files here: https://github.com/nfnitloop/nostr-cli/  (The `nt upload` command.)

Since clients might not know how to put together multipart files yet, my relay (wss://www.nfnitloop.com/nostr/) also makes them available via HTTP. For example:

https://www.nfnitloop.com/nostr/files/bee3454df946e79724b0ec972242ba2d2e3a1fc185931a7a45def81a5ffb194c/

There is room for space-saving in the storage implementation. If this takes off I'd want to store the BLOBs in binary instead of Base64. And I'd probably want to dedupe whole files so that we don't end up having to store multiple copies of them if multiple people upload them. BUT, that can come later without changing the public interface.

Before *that*, I want to implement HTTP range requests to show that the blockSize metadata allows a client or server to efficiently fetch bytes from a known offset. That way you could, for example, scrub to a certain position in a video without having to download all the bytes up to that point. 
 And now I’ve added HTTP Range support.

❤️ to the axum-range crate which made this much simpler. (Though I did have to delve deeper into Rust async than I have before. But I was happy to learn more there.)

Here’s a sample video which I saw making the rounds on Mastodon earlier this week:

https://nfnitloop.com/nostr/files/fb8e82bed22cf9c8ee39ef2898970beee6fec7ca9068e1506a061f22f5ec1ae7/

Note that only the first part of the video is fetched. You can jump past that and the server will start loading from the exact point you jump to.

The algorithm the server uses to answer these range requests could also be implemented client-side. (Say, as the Blob interface in the browser.) I’m just not implementing a client. (Yet. 😉) 
 I’m writing my own Nostr client. (Not entirely from scratch. There seem to be pretty nice libraries out there in TS and Rust!)

But I found 2 annoying characteristics of the relay protocol:

The “notice” message isn’t tied to any particular request or subscription.  But some relays use it as a way to give error messages.

Ex: I found one relay would answer my search subscription with a notice “error: slow down”. Then it never resolves the original request. (Despite IIRC the NIP saying that you should always close a subscription.)

So now I need to add special cases to handle that relay/message. And probably a timeout for subscriptions. Which you can’t do in the general case. But I guess if you don’t get an event or an EOSE within some time you can assume the subscription failed to start?

But how do I know when I’ve “slowed down” enough? Why wouldn’t the relay just throttle its responses to me if it wants me to go slower? 😣

OTOH I guess you have to do this kind of defensive programming for any protocol. But would be nice if there were a standard way to handle cases like this. 




#nostrdev 
 Where’s the #Nostr #queer community? I need less #btc and more #LGBTQ. 🏳️‍🌈🏳️‍⚧️👋🏼  
 Has anyone ever used bookmarks on Nostr? 
 Yes, but, being new, I’m not sure whether bookmarks are (as I assume) purely client-side or pushed to the server.

I end up just liking things, because then I can be sure to be able to fetch those likes, whichever client I’m on.  
 On my way to feed the cat, he ran under my feet and I stepped on his paw. 😞

When I put his food bowl down, full of food, he ran away from me. 😩💔    
 Hey #nostrdev - before I start writing my own, is there a SSR web client for #Nostr that will just read from a single relay?

I’m experimenting with a usage model that’s a bit more like old offline forum/BBS/news readers, or scuttlebutt, where you download the content you want, then can peruse it later, offline. Then you can favorite, reply, post, etc., and sync it back to other relays when you’re back online.

But it seems most clients are very “web 2.0” and want to reach out to multiple relays client-side to pull everything together.

Basically I want something like njump.me but more focused on being a “feed” with some client functionality rather than a lookup tool. 
 No me importa if you want my timeline in English. Ich bin ein multilingual motherfucker cabron. 
 Por suspuesto. Oni parolu kiujn ajn lingvojn they want on their own account. 😀  
 I was just testing this yesterday, hoping to find that clients would render a data URL embedded in kind1 content. None of the clients I tested did, though.

Storing file hashes and content in events makes sense to me. If there are clients that support it I’d love to see what they’ve implemented. I was thinking of experimenting with it this week(end).  
 Not me, I’m a newbie. What was in NIP-95? 
#nostrdev 
 https://pluralistic.net/2022/08/21/great-taylors-ghost/

“The decline of worker productivity in pursuit of metrics is an inescapable failure mode of bossware.” 
 Ok following #nostr is pointless because people just tag it on everything because… they’re posting to Nostr? They want more reach? 🤷🏼‍♂️

Is there another tag that’s less spammy? I propose #nostrdev if one doesn’t exist already.  
 #nostr notes have a created_at “unix timestamp”. I assume that’s in UTC?

Is there a way for a client to add a user’s time zone offset? That would allow clients to optionally display times in that user’s local time zone.

Sometimes that context matters. 

Ex: If someone posts about their morning/evening, it’s useful to know what time they were talking about it.   “Can’t wait till lunch break.”  Is it 11am or 7am there?   
 Picked up #Helldivers2 this week after hearing good things. I’m not big on shooters but I’m enjoying it. 

The over-the-top propaganda and militarization also plays into gameplay mechanics. The people are almost more expendable than the military “toys”. When you die, you respawn into the game as a new recruit. But calling for that machine gun is still on cooldown, so you’d better go pick one up from where that previous soldier (you) died. 😬

The community is friendly though. Probably because it’s PvE and not PvP. Though, friendly fire is enabled. I’m surprised I haven’t seen more griefers.

#gaming #videogames 
 How do #Nostr relays deal with obscene content? Thankfully, I haven't seen any, nor do I think No... 
 I’ve definitely come across “obscene” content. If you browse a live stream of what random people are posting to relays, it’s there.

Worse, most clients seem to download media by default. Since media is stored on third-party http servers, your browser has to make a DNS request for the server and open up a connection to fetch it.

If that content is illegal in your location … well, hope your ISP doesn’t have enough info to turn you in. 🤷🏼‍♂️ 
 Ok, I give up. How do I get my secret key out of the Nostur iOS app? 
 Thanks! I never would’ve guessed it was under *edit*, since you can’t edit your private key. 
 Woo, got my NIP-19 checkmark. ✅  
 Er, NIP-5. I had too many tabs open. 😅  
 Hello everyone! Despite being a distributed social network enthusiast, I somehow only learned of Nostr this week.

I actually wrote (and still use) an experimental distributed social network called FeoBlog. I’ve been happy to see that many of the features I’d implemented there are also present in Nostr. But there’s actually a large community here. 😅

I’ve already started writing some tools for working with Nostr, partly as a way to learn more about the protocol. See my replies here for some of my initial questions. Looking forward to making some connections here! 
#introductions  
 Am I reading the documentation for the socket protocol for relays correctly? Does it always fall back to streaming mode after you run a search? So the way to just do a search without streaming is to cancel the subscription after you’ve received the notice that you’re entering streaming mode?  
 What’s with the Nostr/Bitcoin overlap? I’m not a big fan of cryptocurrency or blockchain-all-the-things. And Nostr itself (thankfully) isn’t implemented atop a blockchain. 
 There seem to be multiple relays that are just open and accept content from strangers. That’s very generous, but how do they cope with spam and abuse? 
 FWIW my FeoBlog implementation dealt with both by only accepting content signed by people who were explicitly allowed on that server, or followed by one of those people.

Are there Nostr relay implementations that can do something similar? 
 I do not. I assume I need to add a wallet address?  
 Nope! Neither. 
 Pli “lerninto” ol “lernanto” sed, saluton!

#esperanto 
 Ankaŭ de tiuj al kiuj ne plaĉas bitcoin. 😆