Yes exactly! 💯 I knew you would be into this. My Ditto project is all about domains. Hoping we can build some awesome communities this way. More examples:

https://image.nostr.build/2037891933d23476e06acd1f82436bec554a403e22e79b40edc45bc439bc9886.png
https://image.nostr.build/9775c51bc7bc0b567bea6da45dbc71784cecb90d36e99305b20bf82c04e5655b.png


People say it works: https://github.com/denoland/deno/issues/8012#issuecomment-1550875329

It is not officially supported. But is Node? 🤔

Deno is much less pain. But I assume if you use an OS even more obscure than Linux you must enjoy the pain. So carry on. 😄


Which is?


Switch to Deno.


nostr:npub18ams6ewn5aj2n3wt2qawzglx9mr4nzksxhvrdc4gzrecw7n5tvjqctp424 Have you seen this? 👀

https://image.nostr.build/95ebc0f0a5f8b5297dd9851c6e0856da8692a8c286c5e34dd705eefd61cb0127.png


It's possible to use nostr.build and Blossom uploaders interchangeably: https://nostrify.dev/upload/#implementations


yo yo yo yo yo yo yo


Paper Mario transgender representation


GM my shit is all fucked


Nice


Danger Mario is the best.


Ideas are cheap. Code is hard. There's no excuse for a tech leader to not also be a coder.


I believe that products should be developer-driven. However, you also need a visionary to create a cohesive vision. Therefore the visionary needs to be a developer.


What the fuck is this? 1705215483996


Are you testing the relay tester right now? Because my relay keeps crashing. 😂


Check: https://docs.soapbox.pub/mostr/troubleshooting/


test


Relatable

https://video.nostr.build/d85928c449a2b752b2848068a3fab8be54afe372582dc11f768369a7f0d71e11.mp4


I'm truly so grateful to have the most amazing, smartest wife of all time. Love you nostr:npub1jvnpg4c6ljadf5t6ry0w9q0rnm4mksde87kglkrc993z46c39axsgq89sc  💜💜💜


The prose outways the cons.


This website is basically usable


It's true. The client needs to be resilient and end the connection itself when the results have been fulfilled. Knowing the intrinsic limit can help that.


Nostr has an optimization problem. Events can exist anywhere on the network, and clients don't know when to stop looking.

This is the same problem the **outbox model** aims to solve, but you can do more than just route authors to specific relays. You can make certain judgements about filters themselves.

On Nostr, different filters have different fulfillment criteria.

For example, take the `ids` filter:

```json
{ "ids": ["1", "2", "3"] }
```

Since events on Nostr are immutable, and IDs are a hash of the event's content, this filter is _inherently_ fulfilled when we have gathered (and verified) all of the events with these IDs.

So, the above filter can be optimized by adding a `limit` equal to the number of IDs:

```json
{ "ids": ["1", "2", "3"], "limit": 3 }
```

The limit is a hint that both clients and relays can use to know when to stop searching. For example, a client who sends this REQ to five relays can short-circuit as soon as the 3 events have been found from _any_ relays, without having to wait for all five relays to EOSE.

## Replaceable events by author

A less obvious optimization concerns filters for replaceable events by author, like this:

```json
{ "kinds": [0], "authors": ["alex", "fiatjaf"] }
```

Kind 0 is a replaceable event, meaning relays should only serve one event of that kind for each author. As a result, the intrinsic limit of the filter is equal to the number of _authors_ in the filter:

```json
{ "kinds": [0], "authors": ["alex", "fiatjaf"], "limit": 2 }
```

When filtering by multiple replaceable kinds, the intrinsic limit is the number of authors multipied by the number of kinds:

```json
{ "kinds": [0, 3], "authors": ["alex", "fiatjaf"], "limit": 4 }
```

Adding a single non-replaceable kind to filter makes us lose knowledge about its limit, making the filter potentially infinite again:

```json
{ "kinds": [0, 3, 1], "authors": ["alex", "fiatjaf"] }
```

## Empty filters

A final optimization allows clients to avoid sending filters to a relay at all, because the filter's intrinsic limit is **0**. Those filters are listed below.

A filter with an empty kinds array:

```json
{ "kinds": [] }
```

A filter with an empty authors array:

```json
{ "authors": [] }
```

A filter with an empty ids array:

```json
{ "ids": [] }
```

Any other filter that contains contains an empty `kinds`, `authors` or `ids` always has an instrinsic limit of 0:

```json
{ "kinds": [], "authors": ["alex", "fiatjaf"] }
```

(It doesn't matter that you're searching for authors here. Since no kinds can match, the filter has an intrinsic limit of 0.)

All of these filters can be optimized by adding `"limit": 0` to them.

Clients can optimize empty filters by not sending them to relays at all, and relays can optimize them by not querying their database. In both cases, an empty result set is returned.

## nostr-tools

A function to calculate the intrinsic limit of a filter has been [added to nostr-tools](https://github.com/nbd-wtf/nostr-tools/pull/340). It looks like this:

```ts
/** Calculate the intrinsic limit of a filter. This function may return `Infinity`. */
function getFilterLimit(filter: Filter): number {
  if (filter.ids && !filter.ids.length) return 0
  if (filter.kinds && !filter.kinds.length) return 0
  if (filter.authors && !filter.authors.length) return 0

  return Math.min(
    Math.max(0, filter.limit ?? Infinity),
    filter.ids?.length ?? Infinity,
    filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind))
      ? filter.authors.length * filter.kinds.length
      : Infinity,
  )
}
```

To use it, you can import it from `nostr-tools`:

```ts
import { getFilterLimit } from 'nostr-tools';
```

Then before you query a relay, you can modify the filters to add `limit`s, and remove any empty filters:

```ts
/** Query a relay using Nostr filters. */
function query(filters: NostrFilter[]): Promise<NostrEvent[]> {
  filters = optimizeFilters(filters);
  return relay.query(filters); // call the relay
}

/** Add a `limit` to each filter if possible, and remove filters that can't produce any events. */
function optimizeFilters(filters: NostrFilter[]): NostrFilter[] {
  return filters.reduce<NostrFilter[]>((acc, filter) => {
    const limit = getFilterLimit(filter);
    if (limit > 0) {
      acc.push(limit === Infinity ? filter : { ...filter, limit });
    }
    return acc;
  }, []);
}
```

## Combined with the Outbox Model

While `ids` filters are inherently fulfilled when the intrinsic limit is reached, replaceable events by authors can have multiple versions of the events on different relays. So adding a limit does not completely solve the problem on its own.

But combined with an Outbox approach, where specific relays are selected for specific authors, we should be able to stop querying when the intrinsic limit is reached, with reasonable confidence that we have the latest version (or one of the latest versions) of the events. This combined approach helps us resolve content faster and more accurately.

Improve performance of Nostr clients and relays by analyzing filters and limiting results.

This is a long form article, you can read it in https://habla.news/a/naddr1qvzqqqr4gupzqprpljlvcnpnw3pejvkkhrc3y6wvmd7vjuad0fg2ud3dky66gaxaqqfkummnw3ez6enfd36x2unn94kxjmtfwsqrkey5


Can't stop thinking about Blossom.


I describe it as a Nostr "server" instead of client or relay. It's both.


Ditto implements the Mastodon API so you can use Mastodon apps with it. It's also a Nostr relay.


It remembers me now when the page loads, that's nice


yo


[nak](https://github.com/fiatjaf/nak) is an awesome command-line tool written in Rust (jk, written in Go) for doing Nostr stuff. I use it on a daily basis to talk to relays and gather events.

Using nak, you can sync your data between one relay and another like this:

```sh
nak req -a <pubkey> <relay-1> | nak event <relay-2>
```

For example:

```sh
nak req -a 0461fcbecc4c3374439932d6b8f11269ccdb7cc973ad7a50ae362db135a474dd wss://gleasonator.dev/relay | nak event wss://slime.church/relay
```

This downloads all data by the **author** (`-a`) from relay 1, and pipes it into relay 2.

Most relays only return about 100 events at a time, though. To sync _everything_ you will need to implement some sort of pagination with `since` and `until` filters.

Most commonly I just need to sync my profile data to a new relay, which can be done with the `-k` (kind) flag. You can pass multiple `-k` flags, eg:

```sh
nak req -k 0 -k 3 -k 10000 -k 10001 -k 10002 -k 10003 -a 0461fcbecc4c3374439932d6b8f11269ccdb7cc973ad7a50ae362db135a474dd wss://gleasonator.dev/relay | nak event wss://slime.church/relay
```

This will migrate your profile, your follow list, your relay list, your mute list, pinned posts, and your bookmarks to the new relay.

If you find yourself doing this often, you can drop a little alias in your bashrc/zshrc:

```sh
alias nsync="nak req -k 0 -k 3 -k 10000 -k 10001 -k 10002 -k 10003 -a 0461fcbecc4c3374439932d6b8f11269ccdb7cc973ad7a50ae362db135a474dd wss://gleasonator.dev/relay | nak event"
```

Then you'll just `nsync <relay>` to copy data from your main relay to another relay!

This is a long form article, you can read it in https://habla.news/a/naddr1qvzqqqr4gupzqprpljlvcnpnw3pejvkkhrc3y6wvmd7vjuad0fg2ud3dky66gaxaqqyxuctt94ehjmnrfqzdr3


My Little Pony Hitler


nostr:npub1w4uswmv6lu9yel005l3qgheysmr7tk9uvwluddznju3nuxalevvs2d0jr5 nostr:npub1w4uswmv6lu9yel005l3qgheysmr7tk9uvwluddznju3nuxalevvs2d0jr5 nostr:npub1useke4f9maul5nf67dj0m9sq6jcsmnjzzk4ycvldwl4qss35fvgqjdk5ks hello world


hello


Yes, it reminds me of Truth Social.


GM Google drive


yolo


I want a Game Boy Nostr signer.


I beat Hollow Knight in Steel Soul mode (without dying)

https://steamcommunity.com/id/gleasonator/stats/367520/achievements/


kim jong un trump mcdonalds


hello world


Should I click this button? Seems important.


The Big Lebowski is not a good movie.


GM, I stringified nip46 params


Supporting Palestine is the stupidest thing ever. Or Israel.


The term "white paper" is so stupid. All paper is white.


Can't sleep... looking through Osama Bin Laden's hard drive.


GM fellows


hi


Just realized Blue Oyster Cult was talking about the mushrooms.


Sounds can be produced at any frequency. Sheet music is just a protocol.


I dreamed about Japan.


It's raining. Rain is good.


Hello


yolo


Why did you call yourself a name that already exists? Have you never heard of the Fediverse before? Or are you trying to intentionally cause confusion?