Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

UNAVI is a peer-to-peer platform for 3D hyperspace, and the reference implementation of The Wired — the spatial hypermedia layer of the Internet. Where the Web links two-dimensional hypertext documents, The Wired links three-dimensional spaces, the objects inside them, and the agents that move between them.

The Wired treats hyperspace as a real place. Its spaces have depth and scale, and what happens in them lasts. That is the claim the whole protocol is built to keep.

A 3D Internet

Hyperspace is a network of 3D spaces. Portals connect them, and a portal carries an agent from one into the next without a reload. Everything in a space is a document. A document takes the form of an object: it holds content, carries the script that defines what it does, and sits at a position in the space. Data, behavior, and presentation share one model, so files, applications, identity, and the network each take a spatial form.

Peer to Peer

Nothing is centrally hosted. Every space, and everything in it, lives on a peer — a personal machine, someone else’s, or a server — and peers sync directly with one another. The Wired is an open standard others can build against; the client and the rest of the stack are open source.

Scripted Behavior

Objects carry behavior with them, written as scripts. A scripted object reacts, animates, and responds to touch, and it works the same way in any space it is carried into.

One Identity

A single identity spans the network, rooted in a DID (decentralized identifier) and traced to a key or a domain the person controls. The same identity, and the identity documents it resolves to, follow the person across every space and store.

Principles

Four properties of the design recur through the protocol:

  • Open — the protocol is a documented public standard, and no permission is needed to build against it.
  • User-sovereign — you hold the key to each of your documents, so you decide who can read and write them.
  • Interoperable — the protocol defines a document’s format, so any implementation can store and serve the same documents.
  • Generative — an object carries its script with it, so a space grows as participants add objects that bring their own behavior.

Protocol covers how these pieces work and how to build on them.

Social

The Wired’s social layer is built on decentralized identifiers (DIDs). People, stores, and servers each have one. A DID resolves to a document listing the public keys and services that identity controls, and a store or peer decides what to accept by checking a signature against those keys.

Identity

A DID (decentralized identifier) is the root of identity in The Wired. It is a globally unique string that resolves to a document listing the keys and services that identity controls.

Methods

The method is the part of a DID after did:. It determines how the DID resolves.

  • did:key is derived entirely from a cryptographic keypair, with no server or domain required. It is portable and self-contained, but not recoverable if the key is lost. Clients generate one for a person by default.
  • did:web is tied to a domain, resolved by fetching https://<domain>/.well-known/did.json. It is recovered through ordinary domain administration, and can advertise services through standard web infrastructure. Servers use one for themselves.

DID Documents

Resolving a DID returns its DID document: a signed record naming the key used to authenticate as that identity, and the services it runs. A store advertises itself there — its network address, and the ID of the identity’s root document.

// https://example.com/.well-known/did.json
{
  "id": "did:web:example.com",
  "verificationMethod": [{
    "id": "did:web:example.com#key",
    "type": "JsonWebKey2020",
    "controller": "did:web:example.com",
    "publicKeyJwk": { "...": "..." }
  }],
  "authentication": ["did:web:example.com#key"],
  "service": [{
    "id": "wds",
    "type": ["WDSEndpoint"],
    "serviceEndpoint": ["<network address>"]
  }]
}

Data

The Wired’s data layer is a personal data store called the WDS (Wired Data Store). Everything a person owns is kept in a store: their spaces, objects, avatars, and files. A store can run on a person’s own machine, on a server they control, or on a server someone else operates for them. One person’s data can live in several stores at once. By default a store runs locally alongside the UNAVI client and syncs to a remote store, which keeps the data reachable while the client is offline.

A store holds two kinds of thing.

  • Blobs are fixed sequences of bytes, identified by the hash of those bytes. All content is a blob.
  • Documents are maps from keys to blobs, signed by their writers. They are the only thing in a store that changes.

Spaces, avatars, and profiles are conventions for naming and arranging documents and blobs.

Documents

A document is a map from string keys to blobs. It is the only mutable thing in The Wired; a blob’s bytes never change, so all change is a document’s keys pointing at different blobs. Writing to a key appends a signed entry naming the blob that key now holds, and a reader of the key sees the most recent entry.

A document is identified by a keypair generated when it is created. The public half is its ID, and the secret half is the right to write to it.

Capabilities

Access to a document is determined by which key you hold.

  • Holding a document’s ID lets you read it and sync it.
  • Holding its secret lets you write to it.

Sharing access means sharing a key. A document is private when its ID was never given out. There is no permission list for a host to misread, leak, or ignore, and no way to grant access it was never given.

Revoking access means rotating: create a new document, move the content, and stop publishing the old ID. Anyone who already read the old contents keeps them, but sees nothing further.

Verification

Every entry is signed by the document’s key and by the key of the device that wrote it. Device keys are bound to a DID, tracing an entry to the person who wrote it.

You can verify data without trusting whoever handed it to you. A store relaying a document cannot alter an entry, insert one, or attribute it to a different author; the worst it can do is withhold the newest version or refuse to respond.

Collaborative Editing

Concurrent writes to a document merge. Each entry carries its author and a timestamp; concurrent writes to different keys all survive, and concurrent writes to the same key settle on the latest. The rule is deterministic, so every peer converges on the same result regardless of arrival order, and editing offline never requires resolving a conflict on reconnect.

An HSD scene is a document with every object and property as a key, so two people moving different objects write different keys and both edits survive.

Format

An entry is (document, author, key) → (hash, size, timestamp), signed by both the document’s key and the author’s device key. The hash addresses the entry’s content in the blob layer; the timestamp orders concurrent writes to the same key.

Blobs

A blob is a fixed sequence of bytes, identified by the hash of those bytes. All content in a store is a blob: profiles, mesh geometry, textures, compiled scripts. A blob’s bytes never change, and its hash is its only name.

Because the ID is the hash:

  • the same bytes are stored once, no matter how many spaces reference them;
  • integrity is checkable by recomputing the hash;
  • anyone holding the hash can fetch it from any store that has it.

Editing an asset produces a new blob with a new hash; references to the old one keep working.

Documents carry their heavier content as blobs. An entry’s content is the bytes’ hash, not the bytes themselves, so a store that holds a document holds everything it needs without parsing its formats.

Retention

A store keeps bytes only while something it hosts references them, or while they are pinned: an explicit request to hold a blob for a stated duration. When a pin expires, the store reclaims the space. A store bounds its own disk without judging what the content is.

Hosting

Your data is reachable only while a store holding it is online.

Local First

Edits can be made offline and synced on reconnect. The local instance also caches external data you fetch, keeping a copy on disk. Peers sync directly with each other when both are online; a hosted store is just another peer, one that stays awake.

Availability

Asking a store to host a document is how it stays available while your devices are asleep. A host replicates the documents it agreed to host, pins their content so it is not garbage collected, and counts the bytes against your quota — without interpreting what it stores. It sees sizes and hashes, never meaning, so there is no format it can reject and no schema it can force.

Hosting provides availability. It says nothing about who can find your data; that is discovery.

Authentication

Stores use DIDs to authenticate and verify data. Connecting to a store proves who you are through a challenge: the store sends a random nonce, and you return it signed by a key in your DID document. The signature covers the specific store and expires shortly, so a captured response cannot be replayed.

Authentication governs what you may ask a store to do: host, pin, spend quota. Protecting your data is a property of the documents themselves.

Discovery

Discovery covers two cases: finding data whose owner is already known, and being found by someone who does not know the owner.

From an Identity

Resolving a DID returns its document, which advertises the network address of a store and the ID of that identity’s root document — a personal index whose keys name the documents holding the person’s profile, avatars, and published spaces. Looking someone up means resolving the DID and syncing the root document; no search service is queried. Because the root document’s ID is part of the identity, it is self-certifying: any store can serve a copy, and none can tamper with it.

Registries

Nothing points at a space until it is listed. A registry is a service that collects submissions and publishes them as ordinary documents called viewsrecent, featured, and per-category listings. A registry runs standalone or as a feature of a data store. Views are what clients sync.

They are capped, so syncing one costs a bounded amount regardless of how much the registry has accumulated. Entries are stored in the registry’s intended order. Because views are ordinary documents, anyone can read, cache, or mirror them without an account.

Publishing

Publishing means submitting an entry to a registry. An entry states what the thing is, where it lives, and how to describe it: title, tags, and preview image. Submissions carry an expiry; refreshing one keeps it listed, so abandoned entries fall off without being pruned.

Publishing and hosting are separate steps. Hosting keeps data reachable; publishing makes it findable. A space can be online but unlisted, or listed but unreachable.

Curation

A registry decides what it lists; it cannot misrepresent. Every submission is signed by its author and passed on unchanged, and readers check those signatures themselves. A registry can decline to carry something but cannot forge, alter, or re-attribute an entry. Search, ranking, and moderation are policies a registry adopts, not rules the protocol imposes. A client can follow a different registry without the underlying data moving.

Presence

A listing says a space exists. It says nothing about who’s inside.

A client present in a space broadcasts a signed presence message across that space’s gossip overlay. Every peer in the overlay repeats the message to its neighbors. Peers already in a space learn about each other this way, without querying anyone directly.

Joining that overlay for the first time requires dialing a peer already inside it. A registry supplies that peer: a client asks the registry who is present in a space, then dials the addresses it returns. From there, presence for everyone else in the space comes from gossip, and once it has reached everyone, clients hold direct connections to each other.

Spatial

The Wired’s spatial layer covers 3D environments, the objects placed in them, and the behavior those objects carry between them. An environment is a space, an object is an HSD document, and behavior is a WebAssembly script the object carries.

Spaces

A space is a shared 3D environment. Several HSD documents are loaded into one space at a time. Everyone present sees the same arrangement of them, and they act on each other through physics and events.

Ownership

A space is one document — the space owner’s — plus every other document anchored or instanced into it, each with its own owner. The space owner controls the base environment; each object’s owner controls the object itself, under the same capability rules as any document.

Portals connect spaces. A portal names a destination space and, optionally, a receptor — a specific prim to arrive at. Walking through one moves the local agent into the destination, loading it first if it is not loaded already.

Composition

A space embeds another document’s content the same way any HSD does: through a prefab or an anchor. A portal is a link between two independent spaces. Walking through one changes which space the agent is in, not what either space contains.

Scripts

Objects in The Wired can carry behavior as WebAssembly scripts. A script is called on three fixed entry points: init, once on load; update, once per rendered frame; and fixed-update, on a fixed interval independent of frame rate.

Scripts are sandboxed. They act on the world only through the interfaces a host exposes to them, with no ambient network or filesystem access.

Trust

A script is granted a fixed set of interfaces when loaded — a capability list, fixed at load time. The space owner decides which documents, and therefore which scripts, are loaded into a space.

Portability

Scripts compile to a single WebAssembly binary, stored as a blob and loaded from that one blob by any UNAVI client — desktop, VR, or web.

HSD

An HSD (hyper-space document) is a 3D scene: the objects in it, their geometry, materials, physics, and scripts. An HSD is stored as a document, and can be placed into any space. A whole environment is an HSD, and so is a single object inside it.

Structure

A scene is a tree of prims — the primitives a 3D environment is built from. Each prim carries properties of two kinds: attributes hold data (a transform, a mesh, a material, physics settings, a script, or a shader graph), and relationships point at other prims, so a surface can take its material from elsewhere in the scene.

An HSD scene is the document: every prim is a key, every property is a key, and the tree is expressed by each prim naming its parent.

Every prim has an identity that outlives editing. Move, rename, reparent, or replace its geometry and it remains the same prim; anything pointing at it keeps pointing at it.

Assets

Heavy content — mesh geometry, textures, compiled scripts — is stored as blobs, each an entry of the document in its own right. Keeping bulk out of the structure lets a scene load first, with its assets following, reusing anything already on disk.

Composition

Scenes are built out of other scenes in two ways.

A prefab is a compiled HSD referenced by a prim and instantiated as a document beneath it. Because a prefab is content-addressed and its prims are given their identities when compiled, everyone who loads it gets the same scene with the same prims, and a prim in one space can point at a specific prim in it.

Anchoring attaches a document’s root to a prim of another document. Anchoring joins a document to a scene without editing either. It is per-peer, so a tool follows the hand that holds it.

Documents are never nested as data. A scene composes others by referring to them, so no arrangement of documents traps one inside another, and no document is rewritten to be used somewhere new.

Collaboration

Because an HSD is a document, it merges by the same rules as any document.

Formats

Two files, mirroring the split USD makes between source and package.

  • .hsda is what authors write: readable text referring to models, images, and script sources by path.
  • .hsdz is what it compiles to: a single self-contained package addressed by the hash of its contents.

A .hsdz is what a prefab points at and what you send to give someone an object; once loaded, it is a document like any other.

HSS

An HSS (hyper-space shader) is a shader written as a graph of nodes rather than as source code. A material that needs more than the fixed PBR set references one. The host compiles the graph into WGSL; a script or asset can never supply raw shader text.

Every graph is built from a fixed set of node kinds (Add, Lerp, Fresnel, TextureSample, and others), and each node references only earlier nodes in the same network. A cycle therefore cannot be constructed, and cost is bounded before the graph runs, at most 128 nodes and 4 texture samples per network.

Structure

A graph has two independent networks. The surface network computes the fragment-stage look, either Lit (feeding a standard PBR pass) or Unlit (written straight to the output, for beams, holograms, and other emissive looks a lighting pass cannot produce). The optional displacement network computes a vertex-stage position or normal offset, for effects like curved beams or billboarding.

Public Inputs

A graph exposes up to 16 public inputs — values a prim can override per-instance without recompiling the graph. Two prims can share one compiled graph and still look different, and the compiled material:graph_data bytes are identical between them, so the graphs dedupe in the blob store.

Authoring

A graph is written as an .hss file and referenced from a prim’s material_graph attribute in .hsda:

// glow.hss — an unlit rim glow, tinted by public input 0
(
    public_inputs: [Color((0.1, 0.6, 1.0, 1.0))],
    surface: (
        nodes: [
            Fresnel(power: Const(Float(2.0))),
            Lerp(a: Const(Color((0.0, 0.0, 0.0, 1.0))), b: Input(0), t: Node(0)),
        ],
        output: Unlit((color: Node(1))),
    ),
)
// asset.hsda — this prim overrides input 0 to red
[
    (attributes: (name: "red_glow", material_graph: (
        path: "./glow.hss",
        overrides: {0: Color((1.0, 0.0, 0.0, 1.0))},
    ))),
]