Skip to content

Post

Changing a specification is mostly a migration problem

The Nostr wiki format change was agreed before I touched it. What kept it from merging was that nobody had written the converter, which is a different kind of work from being right.

· 3 min readNostrDeveloper tools

A change to NIP-54, the Nostr wiki specification, is merged: articles are written in Djot instead of Asciidoc. I wrote that change. I did not have the idea, I did not win an argument, and I did not persuade anybody of anything. What I did was write the converter that made the question “and who migrates everything that already exists?” stop being a reason to wait.

The gap between a change everyone agrees with and a change that ships is where most specification work actually lives, and it is not an intellectual gap.

The decision had already been taken

In October 2025, fiatjaf, who created the protocol, opened an issue titled “wikis as djot: this time is different”. The body is one line: “I’m sorry for the AsciiDoc detour.” That is as settled as a design question gets in an open specification. The author of the protocol had changed his mind in public and said so.

The issue was then closed with nothing merged. Not because anyone objected, but because agreeing to a format change and writing one are different tasks, and nobody had picked up the second.

The review was one question

I opened the migration PR, and Vitor Pamplona, who maintains one of the most used Nostr clients, asked the only question that mattered:

Has anybody coded the migration to Djot?

Not “is Djot better”. Not “do we agree”. The specification could be rewritten in an afternoon; the wiki articles that existed in the old format could not. Every one of them lives on relays, published by people who are not reading this pull request and will not rewrite anything by hand. A format change without a migration path is a proposal to break other people’s data and let them deal with it.

That question is the whole review, and it is the correct review.

The migration tool

asciidoc-to-djot parses the Asciidoc AST with @asciidoctor/core and emits Djot through a custom converter backend, rather than trying to rewrite the text with regular expressions. That choice is most of why it works: the constructs that matter in a wiki article are structural: headings, links, and NIP-54’s wikilink syntax. NIP-54 wikilinks in particular have to become Djot reference-style links, [cryptocurrency][] rather than [[cryptocurrency]]. A regex pass handles common constructs and silently corrupts the rest.

The package exposes the converter as both a library and a CLI. I posted it on the pull request as the migration path the review had asked for.

The specification change merged three weeks later.

What the tool did and did not do

It is worth being precise, because the flattering version of this story is not the true one. The converter does not appear in the merged diff: that diff is one file, the NIP itself, 63 lines added and 37 removed. The tool changed nothing about the specification. It changed what a reviewer had to assume about the consequences of merging it.

The transferable part

When an agreed change remains unmerged, the unresolved question is often who pays its migration cost. The useful contribution is not another argument for the change; it is the tool, compatibility layer, or transition plan that keeps existing users from absorbing that cost individually.

The merged specification diff does not contain the converter, but the decision to merge depended on knowing the converter existed. That is a useful way to read specification work: the text names the new rule, while the migration determines whether the ecosystem can adopt it.