DEV Community

Sui Gn
Sui Gn

Posted on

The Namespace Resolution Protocol.

Namespace Resolution Protocol v0.1

neurons.me / suiGn
Status: Draft — Working Document
License: CC0 1.0 Universal — Public Domain


Preamble

This document specifies the Namespace Resolution Protocol (NRP) for the me:// URI scheme.

The NRP defines how a me:// URI is resolved from a symbolic address into a concrete semantic value, across a distributed mesh of surfaces — without a central registry, without a central server, and without requiring persistent connectivity.

It is the protocol that closes the gap between:

  • Semantic resolution — already implemented in this.me: local, mathematical, derivation-based. The kernel resolves me.get("wallet.balance") entirely offline.

  • Topological resolution — the new work: how a requesting surface finds and reaches the surface that holds the target namespace, path, and key material.

These two layers are separate concerns, implemented separately, but always composed in that order:

me://jabellae.cleaker.me[surface:iphone]/wallet.balance
         │                    │                 │
    topological           topological        semantic
    (find the             (find which        (resolve the
     namespace)            surface)           path locally)
Enter fullscreen mode Exit fullscreen mode

The NRP specifies the topological layer. The semantic layer is already specified by this.me.


1. Definitions

Namespace — A named semantic domain. Represented as a human-readable label (e.g., jabellae.cleaker.me). A namespace is owned by whoever holds its root key material. There is no central authority that grants or revokes a namespace.

Surface — A physical or logical runtime context that can hold a .me kernel instance and participate in the mesh. Examples: an iPhone app, a MacBook daemon, a server process, a browser tab. A surface has a name within a namespace.

Surface identity — The cryptographic identifier of a surface within a namespace:

surface_id = hash(namespace_key, surface_name)
Enter fullscreen mode Exit fullscreen mode

This is never the cleartext surface_name. Only holders of namespace_key can derive or verify a surface_id.

Namespace key — The root secret from which all key material in a namespace is derived. Equivalent to the seed in the derivation-based identity model. Never transmitted; always local.

Endpoint descriptor — A transport-layer locator for a surface (IP:port, relay address, onion address, etc.). Stored encrypted inside the surface index. Not the surface's identity — just how to reach it right now.

Surface index — A .me secret space, scoped under the namespace key, that maps surface identities to their current endpoint descriptors. It is the mesh's contact book. Every surface that holds the namespace key can read and write it.

Claim token — A one-time, time-limited token that authorizes a new surface to join a namespace. Generated by an existing surface. Expires. Consumed on first use.

Stealth root — A secret scope root that returns undefined on resolution. This is an honest absence, not an error. The NRP must preserve this distinction at the network level.

Semantic island — A self-contained .me kernel instance holding a fragment of a namespace. Islands do not need to be constantly connected. They interact by exchanging mathematical hashes, not cleartext data.


2. The Two-Layer Model

Resolution of a me:// URI proceeds in two phases, always in this order.

Phase 1 — Topological resolution

Input: namespace + selector
Output: a reachable surface endpoint

The requesting surface must find the surface that can serve the requested path. This involves:

  1. Deriving the target surface_id from the local namespace key and the selector's surface name.

  2. Looking up the endpoint descriptor for that surface_id in the local surface index.

  3. Establishing a connection to that endpoint.

If the surface index does not contain the target surface_id, or if the target surface is unreachable, topological resolution fails. The request does not proceed to Phase 2.

Phase 2 — Semantic resolution

Input: path + key material (if the path is within a secret scope)
Output: the resolved value, or undefined, or a closed failure

Once the requesting surface has a connection to the target surface, it sends a read request for the path. The target surface resolves the path locally using its .me kernel, and returns a disclosure envelope (see Section 6).

Semantic resolution is entirely local to the target surface. The requesting surface never sees the target's kernel state directly — only the disclosure envelope for the specific path requested.


3. Surface Identity and the Surface Index

3.1 Surface identity derivation

A surface's identity within a namespace is:

surface_id = HMAC-SHA256(namespace_key, "surface:" + surface_name)
Enter fullscreen mode Exit fullscreen mode

This produces a 32-byte opaque identifier. It is the surface's public identity on the mesh. Parties without the namespace_key cannot derive it, verify it, or enumerate surfaces in the namespace.

The surface_name is a human-readable label chosen by the surface operator (e.g., "iphone", "macbook", "daemon-home"). It is meaningful only to namespace holders.

3.2 Surface index structure

The surface index is a .me secret space:

me.surfaces["_"]("namespace_key");

// For each known surface:
me.surfaces[surface_id].name("iphone");
me.surfaces[surface_id].endpoint("encrypted_endpoint_descriptor");
me.surfaces[surface_id].last_seen(timestamp);
me.surfaces[surface_id].public_key("surface_ephemeral_pubkey");
Enter fullscreen mode Exit fullscreen mode

The index is stored locally on every surface that holds the namespace key. There is no canonical remote copy. Surfaces sync the index through the mesh on reconnection (see Section 7).

The endpoint_descriptor field is doubly encrypted: first under the namespace key (so only namespace members can read it), and second it may use an ephemeral surface key for transport security. The endpoint is volatile and may change. The surface_id is stable.

3.3 Reading from the surface index

A surface resolves me://[surface:iphone]/some.path as follows:

1. Compute: target_id = HMAC-SHA256(namespace_key, "surface:iphone")
2. Read: endpoint = me("surfaces." + target_id + ".endpoint")
3. If endpoint is undefined → surface not found → fail with NRP_ERROR_SURFACE_NOT_FOUND
4. Decrypt endpoint descriptor → get transport address
5. Proceed to Phase 2 (semantic resolution)
Enter fullscreen mode Exit fullscreen mode

4. The Claim Ceremony

The claim ceremony is how a new surface joins a namespace. It replaces the need for a central authority to "add" a surface.

The current implementation has a claim token as a temporary in-memory value. This section formalizes and hardens it.

4.1 Token generation

An existing surface in the namespace (the inviting surface) generates a claim token:

nonce         = random_bytes(16)
expiry        = now() + TTL            // recommended TTL: 300 seconds
claim_token   = HMAC-SHA256(namespace_key, nonce + expiry)
claim_payload = base64url(nonce + expiry + claim_token)
Enter fullscreen mode Exit fullscreen mode

The claim payload is encoded into the URI:

me://jabellae.cleaker.me[claim:CLAIM_PAYLOAD]/new-surface
Enter fullscreen mode Exit fullscreen mode

And optionally rendered as a QR code for physical proximity pairing (already implemented in Cleaker).

4.2 Token verification

The new surface presents the claim payload. The inviting surface (or any namespace surface that receives the pairing request) verifies:

1. Decode claim_payload → extract nonce, expiry, presented_token
2. Check: now() < expiry → if expired, reject with NRP_ERROR_CLAIM_EXPIRED
3. Recompute: expected_token = HMAC-SHA256(namespace_key, nonce + expiry)
4. Check: presented_token == expected_token → if not, reject with NRP_ERROR_CLAIM_INVALID
5. Check: token has not been used before (nonce registry, local) → if used, reject with NRP_ERROR_CLAIM_CONSUMED
Enter fullscreen mode Exit fullscreen mode

4.3 Key handshake

After verification, the inviting surface and new surface perform a key handshake:

1. New surface generates an ephemeral keypair: (eph_pub, eph_priv)
2. New surface sends eph_pub to inviting surface
3. Inviting surface encrypts namespace_key with eph_pub:
     encrypted_namespace_key = ECIES(eph_pub, namespace_key)
4. Inviting surface sends encrypted_namespace_key + its own surface_id + current surface index snapshot
5. New surface decrypts with eph_priv → receives namespace_key and surface index
6. New surface registers itself in the surface index:
     surface_id_new = HMAC-SHA256(namespace_key, "surface:" + new_surface_name)
     me.surfaces[surface_id_new].endpoint(...)
     me.surfaces[surface_id_new].public_key(eph_pub)
7. New surface broadcasts its registration to all online surfaces (see Section 5.3)
Enter fullscreen mode Exit fullscreen mode

4.4 Token consumption

The nonce is added to a local consumed-nonce registry after a successful claim. The registry is pruned of entries older than the maximum TTL. This prevents replay attacks.

me.system.consumed_nonces[nonce].at(timestamp);
Enter fullscreen mode Exit fullscreen mode

5. Selector Resolution Rules

The me:// URI selector determines how topological resolution proceeds.

5.1 [current] — current surface

me://jabellae.cleaker.me[current]/profile.name
// or equivalently:
me://profile.name
Enter fullscreen mode Exit fullscreen mode

Resolves entirely locally on the surface receiving the request. No topological resolution needed. Phase 1 is a no-op.

5.2 [surface:name] — specific surface

me://jabellae.cleaker.me[surface:iphone]/runtime.battery
Enter fullscreen mode Exit fullscreen mode
  1. Derive target_id = HMAC-SHA256(namespace_key, "surface:iphone")
  2. Look up endpoint in surface index
  3. If not found: NRP_ERROR_SURFACE_NOT_FOUND
  4. If found but unreachable: NRP_ERROR_SURFACE_UNREACHABLE
  5. If found and reachable: proceed to semantic resolution

5.3 [] — broadcast to all surfaces

me://jabellae.cleaker.me[]/chat
Enter fullscreen mode Exit fullscreen mode
  1. Read the full surface index

  2. Iterate all known surface_id entries

  3. For each entry, attempt to reach the endpoint

  4. Send the request to all reachable surfaces

  5. Collect responses (or timeout)

  6. Offline surfaces are skipped — broadcast is best-effort

  7. The requesting surface may optionally queue undelivered messages for retry (implementation-defined)

Broadcast does not wait for all surfaces to respond. It fires and collects what it can within a configurable timeout window.

5.4 [claim:token] — pairing handshake

me://jabellae.cleaker.me[claim:CLAIM_PAYLOAD]/new-surface
Enter fullscreen mode Exit fullscreen mode

Triggers the claim ceremony (Section 4). Does not proceed to semantic resolution — the claim selector is a control-plane operation, not a data-plane operation.


6. The Disclosure Model

This is what a target surface returns for each category of path request. This model must be implemented consistently across all surfaces. A surface that leaks structural information in error responses breaks the security model.

6.1 Public path

The path exists and is not within any secret scope.

Returns: the resolved value.

{
  "status": "ok",
  "path": "profile.name",
  "value": "José Abella",
  "origin": "public"
}
Enter fullscreen mode Exit fullscreen mode

6.2 Stealth root

The path points to the root of a secret scope (e.g., wallet when wallet is declared with ["_"]).

Returns: undefined. Explicitly. Not a 404, not an error, not a "path not found." The absence is honest and intentional.

{
  "status": "ok",
  "path": "wallet",
  "value": null,
  "origin": "stealth"
}
Enter fullscreen mode Exit fullscreen mode

The distinction between null (stealth root) and NRP_ERROR_PATH_NOT_FOUND (path does not exist at all) is architectural. Callers must not be able to distinguish "this path is secret" from "this path does not exist" — both return a form of undefined. The implementation choice of null vs. omitting the field is left to the transport binding, but the semantics must be indistinguishable to an observer without the secret key.

6.3 Secret leaf — correct key

The path is within a secret scope, and the requesting surface presents the correct key (via the secret:key@ prefix in the URI, or through a pre-established shared secret).

Returns: the resolved value.

{
  "status": "ok",
  "path": "wallet.balance",
  "value": 12480,
  "origin": "stealth"
}
Enter fullscreen mode Exit fullscreen mode

6.4 Secret leaf — wrong or absent key

The path is within a secret scope, and the requesting surface does not present the correct key, or presents no key.

Returns: same envelope as stealth root. The response must be indistinguishable from case 6.2. A wrong key must not produce a different error than no key.

{
  "status": "ok",
  "path": "wallet.balance",
  "value": null,
  "origin": "stealth"
}
Enter fullscreen mode Exit fullscreen mode

This is the "fail closed, leak nothing" invariant. It directly mirrors the ["_"] axiom in the .me kernel (A0 + A2).

6.5 Path does not exist

The path is not declared in the kernel and is not within any secret scope.

Returns:

{
  "status": "not_found",
  "path": "does.not.exist"
}
Enter fullscreen mode Exit fullscreen mode

This is the only case where a not_found status is returned. It must only be returned for genuinely absent paths that are not near any secret scope. If there is any ambiguity about whether a path might be secret, return the stealth envelope (6.2) instead.


7. Surface Index Synchronization

The surface index is local on each surface. When surfaces reconnect after being offline, they may have divergent views of the index. The protocol uses a simple Last-Write-Wins (LWW) strategy, consistent with the .me kernel's A9 axiom (deterministic conflict resolution).

7.1 Sync on reconnect

When surface A reconnects to surface B:

  1. A sends its surface index version vector (a map of surface_id → last_seen_timestamp)
  2. B compares against its own version vector
  3. Each sends the other the entries that are newer on its side
  4. Both apply the updates using LWW on last_seen timestamp

7.2 Surface expiry

A surface entry that has not updated its last_seen timestamp within a configurable window (e.g., 30 days) may be marked stale. Stale surfaces are kept in the index but deprioritized in routing. They are not deleted automatically — deletion requires an explicit ["-"] operation by a namespace holder.

7.3 Endpoint volatility

Endpoint descriptors are volatile. A surface's IP address or relay address may change frequently. Each surface is responsible for updating its own endpoint descriptor in the index whenever its transport address changes, and broadcasting the update to all online surfaces.


8. Error Reference

Code Meaning
NRP_ERROR_SURFACE_NOT_FOUND surface_id not in local index
NRP_ERROR_SURFACE_UNREACHABLE surface_id found but endpoint not reachable
NRP_ERROR_CLAIM_EXPIRED claim token TTL exceeded
NRP_ERROR_CLAIM_INVALID HMAC verification failed
NRP_ERROR_CLAIM_CONSUMED nonce already used
NRP_ERROR_NAMESPACE_UNKNOWN no namespace key available for this namespace
NRP_ERROR_PATH_NOT_FOUND path does not exist and is not near any secret scope
NRP_ERROR_TRANSPORT network-level failure (connection refused, timeout, etc.)

9. What Is Not Specified Here (v0.1 scope)

The following are intentionally deferred to later versions:

Transport binding — This document specifies the protocol semantics, not the wire format. A v0.2 transport binding document will specify the exact message encoding (JSON-over-WebSocket, protobuf, etc.) and the TLS/noise handshake for the surface-to-surface connection.

Relay infrastructure — When two surfaces cannot reach each other directly (NAT, firewall, different networks), a relay is needed. The relay protocol is not specified here. The relay must not be able to read message content — it is a blind forwarder. Relay discovery and selection are deferred.

Namespace key distribution across surfaces — This document assumes the namespace key is already present on the requesting surface. The initial key bootstrap (how the first surface gets the namespace key from the derivation seed) is specified by the .me kernel, not the NRP. Subsequent key sharing is handled by the claim ceremony (Section 4).

Multi-namespace resolution — A surface may hold keys for multiple namespaces. How a surface selects the correct namespace key when a URI omits the namespace qualifier is implementation-defined in v0.1.

Revocation — How to revoke a surface from a namespace (e.g., a lost device) without a central authority is an open problem. A candidate mechanism is a signed revocation entry in the surface index, broadcast to all surfaces. Deferred to v0.2.


10. Design Principles

These are the invariants that any implementation must preserve. They are not implementation details — they are the protocol's soul.

1. No central registry. The surface index is local to each namespace holder. Discovery requires holding the namespace key. There is nothing to hack that reveals a complete list of surfaces or identities.

2. Identity is computation, not storage. A surface's identity is derived from a key and a name. It is not assigned by an authority and not stored in a global directory.

3. Honest absence. A stealth root returns undefined, not an error. The protocol does not reveal the existence of secret scopes to parties without the key. Absence and secrecy are indistinguishable from the outside.

4. Fail closed. Wrong keys produce the same response as absent keys. The protocol leaks no information about why a resolution returned undefined.

5. Minimum surface area. Surfaces exchange only what is necessary. A broadcast sends a path read request — not the full kernel state, not the surface index, not any metadata beyond what the request requires.

6. Mathematical fragmentation. No single surface holds a complete picture of any namespace. The identity is distributed across surfaces by design. Stealing one surface does not compromise the namespace.

7. Offline-capable. Local resolution ([current] or no selector) works with no network access. The protocol degrades gracefully: offline surfaces are skipped in broadcast, missing surfaces return NRP_ERROR_SURFACE_UNREACHABLE. The local kernel is never blocked by network state.


Appendix A — Formal Grammar (ABNF, from URI scheme v1)

me-uri       = "me://" [ namespace ] [ selector ] [ "/" path ]

namespace    = 1*( ALPHA / DIGIT / "." / "_" / "-" )
selector     = "[" ( "current" / "" / "surface:" surface-name / "claim:" token ) "]"
surface-name = 1*( ALPHA / DIGIT / "-" / "_" )
token        = 1*( ALPHA / DIGIT / "-" / "_" )
path         = *( VCHAR / "/" )
Enter fullscreen mode Exit fullscreen mode

The secret:key@namespace prefix for authenticated access is parsed as:

me-uri-auth  = "me://" "secret:" secret-key "@" namespace [ selector ] [ "/" path ]
secret-key   = 1*( ALPHA / DIGIT / "-" / "_" )
Enter fullscreen mode Exit fullscreen mode

Appendix B — Open Questions for v0.2

These are the questions this document intentionally leaves open, in priority order:

  1. Relay protocol — How do two surfaces on different networks find a common relay, and how does the relay stay blind?

  2. Surface revocation — How does a namespace holder revoke a lost or compromised surface without a central authority?

  3. Namespace discovery — Can a surface discover other namespaces it does not already hold keys for? (Current answer: no. Is this the right answer forever?)

  4. Index consistency under partition — If the mesh is partitioned for a long time and surfaces update their own entries, what is the merge strategy beyond LWW?

  5. Derivation-based namespace bootstrap — Should the namespace key be derivable from a BIP-39 mnemonic or similar standard seed phrase, so it can be reconstructed without any network access?

  6. Cross-namespace pointers — The ["->"] operator in .me can create semantic links between namespaces. How does the NRP resolve a path that crosses namespace boundaries?


∴ Witness our seal
suiGn / neurons.me
v0.1 — Draft

Top comments (0)