I'm building a class in TypeScript that manages hundreds of WebSocket connections. Those sockets are Nostr relays.
Most existing Nostr pool libraries are based around finding messages from across all relays in the pool. This library does not do that, and instead focuses on the capabilities of each relay in order to avoid being rate-limited and to optimize network usage.
It is based around the lifecycle of Subscription objects. When a socket dies, it only reopens if a subscription is active. But the lifecycle of the socket itself is separate from the subscription, and a new subscription will not interrupt the state of a socket, eg if it's in exponential backoff. They merely overlap (check each other's state at key moments).
The whole thing is built on web standards. The subscription is an EventTarget, and the system makes heavy use of AbortSignal from top to bottom, greatly simplifying the codebase.
To avoid rate-limiting, it uses semaphores to limit access to each relay, so no one relay is overwhelmed by too many subscriptions. Relays can be individually configured, and it supports loading a giant config map so you can sync with a database to have granular control over relays.
Finally, it is low-level enough to build things on top of it. It requires you to provide explicit relays for each query, and it returns all messages from all relays without any signature verification or validation. Those things, while required for a functioning software, are done outside of the pool for maximum flexibility. It's a feature not a bug.
In conclusion I am building a big Nostr thing that I really need, that nobody has done before in TypeScript. You can see my progress here: https://gitlab.com/soapbox-pub/nostr-machina/-/merge_requests/1
Most existing Nostr pool libraries are based around finding messages from across all relays in the pool. This library does not do that, and instead focuses on the capabilities of each relay in order to avoid being rate-limited and to optimize network usage.
It is based around the lifecycle of Subscription objects. When a socket dies, it only reopens if a subscription is active. But the lifecycle of the socket itself is separate from the subscription, and a new subscription will not interrupt the state of a socket, eg if it's in exponential backoff. They merely overlap (check each other's state at key moments).
The whole thing is built on web standards. The subscription is an EventTarget, and the system makes heavy use of AbortSignal from top to bottom, greatly simplifying the codebase.
To avoid rate-limiting, it uses semaphores to limit access to each relay, so no one relay is overwhelmed by too many subscriptions. Relays can be individually configured, and it supports loading a giant config map so you can sync with a database to have granular control over relays.
Finally, it is low-level enough to build things on top of it. It requires you to provide explicit relays for each query, and it returns all messages from all relays without any signature verification or validation. Those things, while required for a functioning software, are done outside of the pool for maximum flexibility. It's a feature not a bug.
In conclusion I am building a big Nostr thing that I really need, that nobody has done before in TypeScript. You can see my progress here: https://gitlab.com/soapbox-pub/nostr-machina/-/merge_requests/1