Skip to content

links: adopt NomadNet's RRC link grammar (rrc-room-links.md v2) - #4

Merged
thatSFguy merged 1 commit into
masterfrom
feat/rrc-link-grammar-v2
Sep 1, 2026
Merged

thatSFguy merged 1 commit into
masterfrom
feat/rrc-link-grammar-v2

Conversation

@thatSFguy

Copy link
Copy Markdown
Owner

Why

docs/rrc-room-links.md v1 defined rrc@<32hex>:/room/<percent-encoded> and argued that inventing an rrc:// scheme would be an invention "in an ecosystem that already has a convention for exactly this" — NomadNet's nnn@ / lxmf@ page-link shorthands.

The reasoning was right and the fact was wrong. NomadNet 1.2.8 — released 2026-07-24, five weeks before v1 was written — ships a full RRC client and had already claimed both spellings for a different payload grammar:

  • the rrc:// scheme — Browser.py:277-280
  • the rrc@ shorthand, whose expand_shorthands maps to rrc.hub.session and dispatches to the same parser — Browser.py:206-214, :312-314
  • the parser — Browser.py:426-461

v1 was the parallel invention it set out to prevent.

It does not fail cleanly

Transcribing handle_rrc_link and running both grammars through it:

link NomadNet resolves to
rrc://<h>/ops hub <h>, room ops
rrc@<h>/ops hub <h>, room ops
rrc@<h>:/room/ops (v1) hub <h>, room room/ops
rrc@<h> hub <h>, no room

Row 3 raises no error anywhere — this hub accepts any non-empty UTF-8 room name — so a NomadNet user following a link this hub emitted creates and joins a junk room. Percent-encoding compounds it: #off topic shared as off%20topic joins a room literally named off%20topic.

What changed

  • RRCLink.String() emits rrc://<32hex>[:<dest_name>]/<room> with a literal room segment. The percent-encoder is deleted; the decoder stays for v1.
  • ParseRRCLink reads every spelling upstream accepts, plus v1. The shim is unambiguous: a v1 link presents as an empty dest_name (its trailing colon) plus a room of room/<segment>, a shape v2's grammar cannot otherwise produce.
  • RRCLink carries DestName — a link naming another aspect names another destination, and dropping it would silently dial rrc.hub.
  • roomLinkLocked returns "" for a room name containing whitespace. A link in running text ends at the first space; no link is better than one naming a shorter, different room. This is the one thing v1 could express that v2 cannot.
  • docs/rrc-room-links.md rewritten as v2, with §5 recording what happened to v1, and §2.4 recording the loss of the /room/ path namespace as a decision rather than an oversight.
  • internal/service/lxmfnotify.go invite link, README example, three test files.

Checks

go vet ./..., gofmt -l ., go test ./... -count=1, and go test ./internal/{hub,history,peerreg,service} -race -count=1 — all clean locally, i.e. the same set build.yml runs.

Cross-repo

  • reticulum-mobile-app emits and reads v2 from the same day's cut (android-v1.2.114 / ios-v1.0.105).
  • reticulum-specifications #55 documents the grammar in §11.6.3 and withdraws the shorthand proposal that asked to register what already existed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Pu4nRMJbPenrt4gpBp5uPV

v1 of the format (2026-08-29) defined

	rrc@<32hex>:/room/<percent-encoded name>

and argued that inventing an rrc:// scheme would be an invention "in an
ecosystem that already has a convention for exactly this" — meaning
NomadNet's nnn@ / lxmf@ page-link shorthands.

The reasoning was right and the fact was wrong. NomadNet 1.2.8, released
2026-07-24 and five weeks ahead of that document, ships a full RRC client
and had already claimed BOTH spellings for a different payload grammar:
the rrc:// scheme (Browser.py:277-280) and the rrc@ shorthand, whose
expand_shorthands maps to rrc.hub.session and dispatches to the same
parser (:206-214, :312-314). v1 was the parallel invention it set out to
prevent.

It does not fail cleanly. Transcribing handle_rrc_link and running a v1
link through it, the hub resolves correctly and the room name comes out
as the literal string "room/<x>" — and since this hub accepts any
non-empty UTF-8 room name, that is not an error anywhere. A NomadNet user
following a link this hub emitted creates and joins a junk room.
Percent-encoding compounds it: "#off topic" shared as "off%20topic"
joins a room literally named off%20topic.

So the hub now emits

	rrc://<32hex>[:<dest_name>]/<room>

with a LITERAL room segment, and ParseRRCLink reads every spelling
upstream accepts plus v1. The v1 shim is unambiguous: such a link
presents as an EMPTY dest_name (its trailing colon) plus a room of
"room/<segment>", a shape the v2 grammar cannot otherwise produce, since
a v2 link has either no colon at all or a non-empty dest_name. On that
one shape, and only that one, the segment is percent-decoded.

Three consequences, each deliberate:

  - roomLinkLocked returns "" for a room name containing whitespace. A
    link in running text ends at the first space, so it would name a
    shorter, different room; no link is better than a wrong one, and
    this is the one thing v1 could express that v2 cannot.
  - RRCLink carries DestName. A link naming another aspect names another
    destination, and dropping it would silently dial rrc.hub.
  - there is no /room/ path namespace any more; the whole tail is the
    room name. §2.4 records that as a decision, not an oversight.

docs/rrc-room-links.md is rewritten as v2, with §5 documenting what
happened to v1 so the next reader does not re-derive it. The lesson is
narrower than "check upstream first", which v1 did do: it is that
upstream's convention for a NEIGHBOURING problem is not upstream's
convention for yours. v1 read SPEC §11.6.3, generalised the page-link
grammar to a new aspect, and never checked whether the client already
had an RRC-specific answer. It did, in a module the same package ships.

reticulum-mobile-app emits and reads v2 from the same day's cut;
reticulum-specifications PR #55 documents the grammar in §11.6.3 and
withdraws the shorthand proposal that asked to register what already
existed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pu4nRMJbPenrt4gpBp5uPV
@thatSFguy
thatSFguy merged commit a7ca60a into master Sep 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant