OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-social commits / cdf43e4

docs: naming — hashes are unique, names are not global
A namespace is a subscription, not an authority: it is a room you join, and its names resolve for you. That escapes the delegation recursion without a root, because rooms are already discovered socially. Only keys go in bytes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
cdf43e4db8950aa2c590620bcd21bbdcac3712c2
by plankatron <fleeky@gmail.com> · 2026-09-05T22:01:05.000Z

Changed files (2) +154 -0

docs/NAMING.md +153 -0
……@@ -0,0 +1,153 @@
1# Naming — hashes are unique, names are not global
2
3**Status:** design. Nothing built.
4**Date:** 2026-09-05
5**Sits beside:** `SOCIAL-MODEL.md`, which specifies **addressing** and says nothing about **naming**.
6This is that half.
7**Grounded in:** keet's key derivation (`keet-core/lib/core/invitations/index.js`), pgh's
8`NameRegistry` (`pgh/lib/name-registry.js`), and `ROOMS-ARE-THE-COMPUTER.md`'s discovery model.
9
10---
11
12## 0. The claim
13
14> **Hashes are unique. Names are not global. There are global namespaces you can subscribe to.**
15
16That is the whole design. Everything below follows from it.
17
18A namespace is not an authority — it is **a subscription**. You join it the way you join a room, and
19its names become resolvable *to you*. Two people subscribed to different namespaces resolve the same
20string to different things, and that is correct rather than a conflict.
21
22## 1. Why there is no third option
23
24This is Zooko's triangle: a naming system can be **human-meaningful**, **globally unique**, or
25**decentralised** — any two. Every escape attempt pays somewhere:
26
27| system | gives up |
28|---|---|
29| DNS | decentralised — it has a root |
30| content hashes, pubkeys | human-meaningful |
31| petnames | globally unique — each person's namespace differs |
32
33`yourname.topicname` composability is the DNS escape, and it recurses: delegating names requires
34someone to name the delegators. **The recursion only bites if the namespace must resolve through
35something new and global.** It does not — a namespace is a room, and rooms are already resolved
36socially (registry rooms, DM shares, DHT topics, an invite). If you cannot resolve `rust-p2p`, you
37have no social path to that community, which is the correct answer and not a failure.
38
39## 2. Four layers, and only one of them is in the bytes
40
41```
42 layer 3 petnames purely local, yours, override everything never leaves your machine
43 layer 2 namespaces rooms you subscribe to which "alice" you mean
44 layer 1 names unique WITHIN a namespace, by its own rule alice, rust-p2p
45 layer 0 keys unique by construction ⇦ THE ONLY LAYER IN BYTES
46```
47
48**Layer 0 is the only thing a record ever contains.** keet does the same and derives everything from
49one seed through seven domain-separated namespaces; a room key there is a hypercore *manifest* key
50over its first signer (`getWriterKey`), never a chosen string.
51
52⛔ **A name must never appear in a record.** pgh's `NameRegistry` is first-claim-wins and a released
53name is **reclaimable** (`name-registry.js`), so a name embedded in immutable bytes could later denote
54a different person. This is a correctness rule, not an aesthetic one.
55
56## 3. Resolution
57
58```
59alice.rust-p2p
60 │ └── a namespace — a room you subscribed to, found in a registry room, or were handed
61 └──────── a name claimed within it → an identity root → a room key
62```
63
64**"Global" is just the default namespace.** `alice` bare means *alice in whatever namespace you have
65made default*. Same syntax; the bare form is the elided default. Which means **several roots can
66coexist and nobody has to agree** — shipping a default registry makes us a convenient root, never an
67authoritative one, and it must remain replaceable.
68
69**Resolution is local.** Because a namespace is a room you have joined, its contents are already on
70disk. Names never become a liveness dependency for rendering, and a name resolves the same offline.
71
72**The hash never surfaces.** You type `alice.rust-p2p`, not `alice.k7x9f2…`. The key stays in the
73bytes; the name stays on screen — the same rule as `SOCIAL-MODEL.md`, applied one level up, and it
74**recurses**: names resolve through rooms, rooms resolve through rooms, bottoming out at a key someone
75handed you (an invite, a DM share, a QR code) rather than at an authority.
76
77### 3.1 `alice.rust-p2p` is a RESOLUTION PATH, not a container
78
79It means *"alice, as named in the rust-p2p namespace"* — resolving to her identity root and thus her
80personal room. It does **not** mean "alice's stuff inside rust-p2p". Her records in that community
81live at `favela://<rust-p2p-roomKey>/…` like every other member's. The dotted form reads as both, so
82it has to be stated.
83
84### 3.2 Unresolvable names fail VISIBLY
85
86If a namespace is not subscribed, or holds no such name, show the key and say so. **Never guess a
87namespace and never silently pick one.** Fail-closed, the same direction as everything else in this
88model.
89
90## 4. Namespace governance is room governance
91
92Who may claim a name in a namespace = **who may write to that room**. So an open, invite-only or
93curated namespace is a matter of keet roles, and needs no new permission system — the same result
94`SOCIAL-MODEL.md` §6.7 reaches for moderation.
95
96⚠ **A subscription's names can change meaning over time**, because first-claim-wins plus reclaimable
97tombstones means a name can change hands. A client **must surface a reassignment** of a name it has
98previously displayed — the same duty as a key-change warning in a messenger. Silently re-pointing a
99familiar name at a new person is the one failure this design can actually cause.
100
101## 5. Conflicts are legal and legible
102
103Two namespaces may both name `alice`. If you subscribe to both, the client qualifies:
104`alice.rust-p2p` and `alice.berlin`. That is disambiguation, not an error, and it is the visible form
105of the property we chose to give up.
106
107**What we give up, deliberately: global uniqueness.** The compensation is that a name is always
108qualified by a namespace you can point at, and an unqualified name is never authoritative — only
109defaulted.
110
111## 6. Subscription is a pattern this stack already uses twice
112
113- **Moderation signals** — subscribe to a community's flags, apply your own context; the signal is
114 information, never enforcement (`COMMUNITY-MODERATION.md`).
115- **Labelers** — atproto's equivalent, opt-in per consumer.
116
117A namespace is the third. That consistency is the strongest argument that it is the right shape: in
118each case, **what is shared is an offer, and what is authoritative is what you chose to subscribe to.**
119
120## 7. Translating outward
121
122Both target networks already work this way, which is a good sign:
123
124| network | naming |
125|---|---|
126| **ActivityPub** | `@user@domain` — the domain **is** the namespace. Maps directly; a bridge supplies its own domain |
127| **atproto** | handles are DNS names — the same, with DNS as the root |
128| **IRC / Matrix** | server- or homeserver-scoped nicks; namespace-per-server |
129
130Ours differs in one respect only: **the namespace is a room rather than a domain**, so it needs no
131registrar and no DNS. The shape is the same, which is why the projection is uninteresting — and that
132is the point.
133
134## 8. Open
135
1361. **Delimiter and grammar.** `.` reads like DNS but collides with NSID-ish strings; and whether
137 namespaces nest (`alice.rust-p2p.berlin`) or are flat is unsettled. Flat is probably right.
1382. **What we ship as the default namespace**, and how a user replaces it. A default that cannot be
139 changed is a root.
1403. **Namespace discovery.** A namespace is a room, so it inherits room discovery — but "which
141 namespace should I subscribe to" is the same cold-start problem as "which community", unsolved
142 there too (`SOCIAL-MODEL.md` §10).
1434. **Rendering a reassignment** (§4) — what the UI actually shows, and for how long.
1445. **Whether a room itself can hold a name in a namespace**, or only identities. pgh's `NameRegistry`
145 anchors a claim to a `publisher` identity and has no notion of naming a room.
146
147## 9. What this does not claim
148
149- It does not make names globally unique. It says they are not, and builds on that.
150- It does not remove the need for a default namespace; it makes the default **configuration** rather
151 than protocol.
152- It does not solve cold-start discovery of namespaces (§8 #3).
153- It does not put a single name in a single record, ever.
docs/README.md +1 -0
……@@ -17,6 +17,7 @@ P2P social layer built on Hypercore Protocol and Keet. A family of interoperable
1717| [ROADMAP.md](ROADMAP.md) | Evolutionary path from godot-keet to hyper-social — 8 phases, what exists, what to build |
1818| [AUTO-DISCOVERY.md](AUTO-DISCOVERY.md) | Layered auto-discovery design — 5 layers replacing hardcoded registries, privacy analysis |
1919| [AUTO-DISCOVERY-SPEC.md](AUTO-DISCOVERY-SPEC.md) | Implementation spec — code changes for keet-daemon, godot-keet, bridges (~1,400 LOC) |
20| [NAMING.md](NAMING.md) | Hashes are unique, names are not global — there are global namespaces you can subscribe to. Zooko's triangle, four layers, and why only keys go in the bytes. |
2021| [SOCIAL-MODEL.md](SOCIAL-MODEL.md) | **Latest**: our own social model — rooms as scope/audience/governance, visibility as a place not a field, and how it translates outward to AP/atproto/IRC. Sits above favela and every bridge. |
2122| [ROOMS-ARE-THE-COMPUTER.md](ROOMS-ARE-THE-COMPUTER.md) | Bottom-up architecture — rooms as only data structure, profile rooms, local MI computation, no hand-crafted Autobases. Supersedes AUTO-DISCOVERY-SPEC.md |
2223