Potentially unpopular opinion: Nostr should adopt ATProtocol-style lexicons.

ATProtocol lexicons are good because they encourage more detailed Kind schemas.

Let's take the example of NIP-29 chat groups and how the same group can look very different when viewed in 0xchat versus when viewed in Chachi. Want one thing to look right? Open the group in 0xchat. Want another thing to look right? Open the group in Chachi.

In ATProtocol it would be like this. Chachi comes into existence. When it does, 0xchat already has a hyper-detailed lexicon schema (com.0xchat.message) and this lexicon leaves little grey area—maybe some cosmetic grey area and that's about it.

On day one Chachi has to make a choice. Follow this hyper-detailed existing 0xchat lexicon or create a new Chachi lexicon. If going the first route, Chachi declares the 0xchat lexicon in each event that Chachi sends ("$type": "com.0xchat.message”) and by doing so Chachi is saying “I agree with everything in this very detailed schema of theirs and if something isn’t right then I’m the one that’s out of line, it’s me.”

If going the second route, Chachi will not be interoperable with 0xchat.

That's just the choice Chachi has to make. Is it worth it to forge a new path? Or is it better to piggy-back off 0xchat's success? Maybe there are other clients also using 0xchat's lexicon; does Chachi want to be on that bigger team? Or maybe Chachi has a super feature idea and it’s totally fine to go it alone, the users will come for that feature. Either way, Chachi has to make a choice, and it has to make that choice on day one.

You could say that the Nostr way is pretty much the same as the flow I just described above, with only minor semantic differences. Chachi has to choose to follow a NIP (and a Kind) on day one, or to go it alone. Like Flotilla went it alone at the start. But no. It’s not the same in substance. Of course Chachi does have to choose a NIP and a Kind on day one—but Nostr Kinds are like laws in countries where the authorities have left themselves a *lot* of room for interpretation. Singapore this is not. Rather this is more like [insert name of murky country here]. Did you break the law? Maybe you did, maybe you didn’t, depends on who’s asking who and why.

The current Nostr way allows devs to procrastinate, put off the “tidying up” until some undetermined point in the future. And the one who pays for that procrastination is always the user.

In short ATProtocol lexicons both allow for and encourage a significantly higher level of detail and expressiveness—and in doing so they tend to force tidiness from the start. If you take Nostr’s Kind1 schema and compare it to app.bsky.feed.post (their Kind1), and not only that but all the lexicons that app.bsky.feed.post references (such as app.bsky.richtext.facet and app.bsky.embed.images and app.bsky.embed.record ...) it's quite clear we’re looking at an order of magnitude difference in detail. And at the end of the day this is all about detail. That’s where the devil is. And the devil is having a lot of fun on Nostr these days.

If you're an ATProtocol developer and you've got a cool idea for, say, a VR client, and you decide you're going to re-use the app.bsky.feed.post lexicon and all its mandatory entourage—well there's very little wiggle room for you. Either you state outright that this is your lexicon in each event and you stick with the program—or you don't and you make a new lexicon and you declare that new lexicon explicitly in each event. And that’s good. Because over there devs can't just say to users "you know what guys, you figure it out". The devs have to figure it out themselves. And beforehand.

So why the lower level of detail for Nostr?

One reason Nostr Kinds are less opinionated is to increase the chance of being merged on Github. Meaning much of this is psychological (as opposed to technical, given that in theory there is no limit to how detailed a Nostr Kind can be, though there are also technical differences that give ATProtocol lexicons more expressive power). Nostr kinds are discussed—and discussed, and discussed some more. This has a watering down effect.

ATProtocol lexicons are discussed too, but in fewer situations, and the discussions end sooner. Part of this is because there is no two-tier system of “merged” and “un-merged”. (On Nostr an un-merged spec is a little like a second-class citizen, and devs feel a lot of pressure to get their specs merged and first-class-ified). ATProtocol lexicons also start off with devs and their unique ideas—but they aren’t ever merged. You can have a much-copied lexicon, one that say five or six other clients make use of. And you can have a solo lexicon, one used by your client alone. That’s where the difference is. This means that on ATProtocol it’s all about traction. Not traction in order to make the case that your PR should be merged. Just good old traction.

Of course the downside to lexicons is that devs with clients that have good traction gain a lot of lexicon power. If I make an ATProtocol client for publishing serialised fiction called CheeseRead, and my chapter publishing lexicon is com.cheeseread.chapter, and I get a lot of users, and a few other serialised fiction clients emerge (maybe for other operating systems), and they use my com.cheeseread.chapter lexicon too—then I’m in a pretty strong position. And bonus: I get to feel proud of myself because all these other clients have my brand showing up in their event json.

This doesn’t mean I can screw with the lexicon and break things on their end. Under the rules all old data must still be valid under an updated lexicon, and new data must be valid under the old lexicon, so new fields must be optional, non-optional fields cannot be removed, etc. (or a fresh lexicon is needed). But it does mean that it’s harder for other clients to pull users from me because my client is going to be very optimised for this lexicon. (Mind you this can also be a good thing because it means others in the space have to think beyond copy-paste.)

All in all though, there’s something to be learned here. ATProtocol lexicons are more decentralised—due to there being no “PR & merge” concept—and more practical—due to how much grey area they usually remove.

The lesson for Nostr: there is no point in getting everyone on the same page if that page doesn’t have enough words on it.

The ATProtocol lexicon concept is a smart concept that can be borrowed. It encourages what I call “fearless detail”. At any rate Nostr has to move off Github sooner or later, so why not dump both the platform and the flow at the same time? And jump on the lexicon train.

Reply to this note

Please Login to reply.

Discussion

Can you explicitly provide the difference(s) observed with screenshots?

This is the most nuanced take I've seen on this subject yet.

First of all, yes, I agree github is not ideal and we should move NIPs elsewhere. But so far no one has stepped up to build a viable replacement, so for now we have to deal with the two-tier system through culture, by encouraging devs not to worry about things not getting merged. I personally have 10+ unmerged open PRs which are in varying levels of implementation in my stuff. Having them unmerged is 1. an invitation to collaborate, and 2. a signal that they're not adopted widely.

As far as lexicons go, when I was designing my own protocol that was the direction I was taking things. I'm still sympathetic to the idea, except for what you pointed out about control over the lexicon. Numeric identifiers are much better, because they're neutral — *if* we can get into a good habit of publicly signaling their use and adoption. That's not trivial of course, and points back to my first paragraph.

As far as specificity, the terseness and ambiguity is (I think) one of nostr's strengths. I could be wrong about this, because it basically goes against all the conventional wisdom of specification writing, so we'll see if it works out in practice. But specifying the minimum interoperability surface area allows there to be more flexibility in how a given data format can be used. This can lead to inconsistent UX, which is a problem but something devs are motivated to work through because it aligns with their interests, or it can lead to innovative new ideas. In either case though, it forces devs to be more conservative about how they treat events, encouraging defensive coding (in case of deliberately malformed events as an attack) and wider interoperability. This is an adaptive approach rather than a validate-and-throw-away approach.

I think your argument is by far the strongest for what we have now (neutral labelling, flexibility, wide berth, better of a petri dish for new ideas...). The biggest issue for me is that this more ambiguous approach pushes too much of the cognitive load over to users. Many of these benefits accrue to the devs directly and to the users in a roundabout way. Better the opposite I think, have the benefits accrue to the users directly and to the devs in a roundabout way.

Also while I agree devs are motivated to work through the inconsistent UX, it becomes hard to channel that motivation to concrete action. You have 3 clients doing it one way, 3 clients doing it another way, and 3 clients yet another way. All 9 are hearing complaints from users and feeling motivated to work things out, but work things out how? Often it becomes a very tense and drawn-out game of paper scissors rock.

I think a lot of this falls into the class of problems for which no later decision can make up for the absence of an earlier decision. And lexicons do force those decisions much earlier.

Great write up sir!

I think we can get the best of both on Nostr. Withe the **onNostr** being the important part.

The central repo on GitHub = :110percent: the issue. As soon as you take that of the picture and imagine a Nostr spec on Nostr, there's opportunity to introduce the best of what lexicons bring.

I'm personally leaning towards:

1️⃣ Wikis as the best way to publish specs

2️⃣ App Releases as a potentially more suited place to reference which specs your app is built on

That way I can:

- have my detailed Kind 9 Chat message "lexicon"

- document it in a wiki that doesn't need to be merged necessarily (yet still becomes more valuable, the more it is referenced/zapped/replied/accepted in communities/....)

- reference it transparently at the app-level without having to stipulate in each message what spec is being used (which comes with privacy issues etc...)

How would you do lexicons btw, in a way that's backwards compatible? nostr:npub1hyxredcavc6ruqgsf4wf4hmakpwnvefmzaspl7dja6a2sxlx0q3sxwtqnx

Over there the protocol simply mandates both backwards and forwards compatibility. But often times the thing to is to reference another lexicon, or to create a sub schema.

So let's say in a lexicon called:

com.example.post

I have an author field, and for that field I want to reference the profile definition in another lexicon called com.example.user, I pop in:

"author": {"type": "ref", "ref": "com.example.user#/defs/profile"}

And that references:

"profile": {

"type": "object",

"properties": {

"handle": {"type": "string"},

"displayName": {"type": "string", "maxLength": 256},

"avatar": {"type": "string", "format": "uri", "nullable": true}

}

Then it's all just lego blocks and you can worry about them block by block.

But even if you have a breaking change and you come up with a new lexicon for it, all the other apps using the old lexicon will continue to use it, it can't be deleted, so you've not broken anything on their end.

Lo, they think they're messy :winkwithtongue:

> 2️⃣ App Releases as a potentially more suited place to reference which specs your app is built on

Makes a lot of sense to me. Without that real-world context you can be forced into a kind of lowest-common-denominator-of-possibilities outlook.

True story!

Will pitch the idea to nostr:npub1wf4pufsucer5va8g9p0rj5dnhvfeh6d8w0g6eayaep5dhps6rsgs43dgh9 , but it's not priority now.

Step one = Getting the communities running where we can collab on these NIPs in the first place.

For sure. If you get it up I'm happy to test sending join events with some throwaway nostr:npubs. This here web UI seems like it'd make testing pretty easy for a small test group.

https://nostrtool.com/

There is no point in getting everyone on the same page if you can't even find the page on here. Lol :haha:

Nice try, CIA.

Back at you, CEO.

I actually adopted this for Pubky, but I still think it is useless... no one has any incentive in detailing their schemas just so their competitors can steal market share easier.

Interoperability will never work, because there is no incentive.

I'm for anything that weeds out this "why not?" type of interop that just dials up the cognitive load for end users. The kind where the end user has to open a post in12 clients and then assemble a serial killer hunting pin-board with strings and polaroid photos to figure out what the original poster's intent might have been.

But I do think there is incentive.

In an embed context (my lexicon represents something non-core in your app) there are incentives both ways, you get your stuff embedded, I get that added info for my users, win win.

In a piggyback context (one app clearly just riffing of another) there's incentive to piggy back, and there's incentive to be piggy-backed off of, so also win win.

In most other contexts, probably no. So very context dependent.

Being able to embed stuff has nothing to do with interoperability or lexicons it just means open APIs for extensions/widgets.

Unix apps can be piped together with a common stdin/stdout interfaces... but they don't have to publish specs on how do they behave.

>Being able to embed stuff has nothing to do with interoperability or lexicons

What do you mean, sure it does. Example these comments are embedded bsky.app posts (via the bsky.app lexocon) on the whtwnd.com long-form client (which has it's own longform lexocon). And that's a win win, both sides have said.

That's