How Udanax Gold solved hypertext's hard parts — and where a modern engine takes them further
In 1988, a small Autodesk-funded team set out to build Ted Nelson's deep hypertext system for real. The design — tumblers, enfilades, transclusion, unbreakable links — was decades ahead of what hardware and infrastructure could support. What the team produced, released as open source in 1999 under the names Udanax Green and Gold, was a set of solutions to twelve genuinely difficult problems. The web shipped the simple versions instead and won. But the twelve problems never went away: every document system that wants quoting without drifting, links without rotting, or history without archaeology eventually hits them.
This document walks through each problem the way a developer meets it: the naive approach, why it fails at scale, what the Gold team invented instead, and — where it exists — how a modern implementation (Xudanu, an independent open-source engine in Rust) extends the solutions — sometimes with machinery the 1980s didn't have (CRDTs, deployed cryptographic hashing, Bitcoin timestamping), sometimes by finishing what Xanadu already invented (content addressing was Gold's concept — see Problem 10; what's modern is running it on commodity cryptographic infrastructure).
You insert one character at the top of a book. Why should the other million characters care?
Store text as an array. Every insertion at position p shifts
all n − p elements after it. Insert at the front of a large
document and you touch the entire buffer:
O(n) per edit, O(n²)
to build a document by repeated insertion.
A balanced tree where every node carries a summary of its subtree — the total width (character count) below it. To find or insert at a position, you descend from the root, subtracting widths as you go; only the O(log n) nodes on one path are ever touched.
The trick isn't the tree — it's the discipline: the width summaries must stay exact no matter what happens below. That invariant is what makes everything downstream (positions, mappings, links) work without fixups.
The enfilade lives on (edition/orgl.rs), but the editing layer
is a purpose-built CRDT — so concurrent edits from different machines
converge by construction instead of requiring locks or a
coordinator. Gold had no answer for simultaneous distributed editing;
the modern engine measures ~80µs/op under
concurrent interleaving, roughly 900× the throughput of an earlier
operational-transform-style engine.
Are these two 2-million-character documents identical? The honest answer shouldn't take two million comparisons.
Walk both documents element by element: O(n) to say "identical," and a full diff walk to find where they diverge — every comparison, every time, forever.
Each subtree carries a content summary — a crum (Nelson's word for a crumb). Equal crums mean identical subtrees, full stop: equality becomes O(1). And diffing descends only where crums disagree: O(size of the difference), not O(size of the documents).
The crum is a BLAKE3 hash, computed incrementally and verified by test armor. Subtree crums power revision compare (jump to the first divergent chunk), the change-detection used by collaborative sync, and content-addressed identity (Problem 10). One idea, everywhere.
Insert a paragraph between section 2 and section 3. Now every "section 4" reference in the world is wrong.
Number things sequentially. Any insertion renumbers everything after it, and every external reference to a renumbered thing breaks. The web's answer — URLs naming a location — rots when the location changes. Either way, the address is a hostage to the content's arrangement.
A tumbler is a hierarchical address —
"server".2.1.1004.15.42 — with one crucial property:
between any two tumblers there is always another one. Need an
address between [1] and [2]? Take
[1,1]. Between [1] and [1,1]?
[1,0,1]. The space is infinitely refinable, so new
content allocates between existing content and nothing ever
renumbers.
Tumblers are fully activated as xan:// URIs —
server identity, region prefix, work, span, and a
?rev=N version qualifier on the query layer (paths stay
purely positional — no span/revision ambiguity). Between-allocation
(Sequence::between) is property-tested and backs
region_insert_between. And every work's address carries
its creating server's cryptographic identity, so addresses survive
replication: a replica answers for the origin's address.
xan:// URIs with version qualification and
replication-proof server identity.
A million stored pointers point into your document. You insert one paragraph. How many pointers must you fix?
All of them. Every link target, every quotation range, every bookmark shifts with the insert, so the system must find and adjust each one: O(pointers) per edit, and woe to the pointer you didn't know about.
Store edits as displacements — "everything after position p moves by δ" — attached to enfilade nodes. Positions below stay valid because they're interpreted relative to the displacement, which propagates lazily down the tree only when touched. Better: displacements compose — five sequential inserts become one composed mapping instead of five pointer-fixing sweeps.
Mapping composition and span migration cover the practical cases: links, transclusions, and annotations survive edits. Full wid semantics — lazy in-tree propagation, relative positions in every node — is tracked as deferred work (FR-64-D3), triggered if profiling ever shows migration sweeps becoming hot. Gold's solution was deeper than current need.
"Everything in this project / collection / client's space" — without filtering the whole store every time.
Tag everything with a collection ID and filter on every query: O(store) per question, forever, and the tags themselves are a parallel structure that drifts.
Because addresses are hierarchical, a scope is just an address
prefix: region 2.1 is every tumbler starting
2.1…. Containment is a prefix test on the address
itself — no tags, no scanning, and nesting falls out for free
(region 2 contains 2.1 contains 2.1.5). The hierarchy prunes the
search space physically.
Regions are live server semantics: works created in a region
context allocate their addresses under the region prefix;
containment runs through the Sequence space algebra
(prefixed_by); region members list in address order;
and — beyond Gold's design — write enforcement implements
the permeability principle: reads and transclusions cross region
boundaries, writes don't. One server hosts N isolated docuverses
(classes, clients, experiments) with visible boundaries.
The quotation that updates when its source is corrected — Nelson called this transclusion, and it's the economic heart of the design.
Copy-paste. The copy is dead: when the source is edited or deleted, the quotation silently stops being what it claimed. Every copy is also new storage — the same passage stored a thousand times costs a thousand passages.
Content included by reference: a quotation is an address range pointing into the original. Stored once, live everywhere. Nelson's canonical demo was two windows side by side, the same passage highlighted in each, in different documents — one fact, many homes.
Transclusion is an inline element with per-span provenance and optional BLAKE3 content-hash pinning; cross-server transclusion fetches from the origin's public API and verifies the hash (tamper-evident quotation across trust domains). Two things Gold never had: frozen quotation mode (pin to a revision for citation-stability, with explicit re-fetch), and reference-over-copy suggestions — the system notices when you re-type existing content (within microseconds of six typed words) and offers to turn it into a live transclusion. The discipline becomes an interaction.
"Who links to this passage?" The question that made the web one-way and the wiki a query.
Scan every document for links pointing at the target: O(entire store) per question — which is why the web never answered it at all, and search engines approximate it with crawls.
A link is stored once, as a set of ends — each end an address range in a specific document. Because the link is registered with every work it touches, each end can enumerate its links directly: O(1)-ish lookup from either side. Bidirectionality is a storage property, not a query.
Five built-in link types (comment, reference, disagreement, quotation, see-also) with a type registry; gathered end-sets (one end holds many passages — Gold's FeMultiRef shape); links that can attach to other links (comment-on-connection); and link ends now carry their target's permanent tumbler address, so links survive replication and server boundaries.
Tree nodes per character: 10,000-character page, 10,000 nodes, 10,000 allocations. The tree eats the machine.
One element per character — the "pure" fine-grained model. Correct, and ruinous: memory and constant factors make real documents impractical.
Adjacent same-kind elements merge into runs. Steady-state text is a handful of nodes covering whole paragraphs; splits happen only at edit boundaries, and coalescing re-forms runs as edits settle. The fine-grained model survives logically while the physical storage is coarse.
Inline coalescing with transclusion-boundary awareness (a transclusion never merges into surrounding text — the reference edge is structural), plus chunk-level storage so runs serialize as blocks. The Coles-notes version: logic fine-grained, storage coarse, both true at once.
Two servers hold the same 2GB docuverse. One paragraph changed. What must cross the wire?
The document — or at best a byte diff of serialized forms: O(document) either way, format-fragile, and meaningless across structural changes.
Compare subtree crums at the coarsest level first; descend only where they disagree. Sync cost is proportional to the difference, not the corpus.
Chunk-level crum diff drives revision compare (per-hunk author and time from provenance), collaborative state exchange, and the forensic chain checks. Content-addressed chunks mean the wire carries hashes first, bytes only for misses — the same idea Git uses for objects, applied to document structure.
"Is this the same passage?" — answered by the address, not by trust in whoever's serving it.
Identity by ID number or URL — minted by whoever mints first, meaningless across systems, forgeable by anyone who controls the server. The web's identity crisis in one sentence.
Identity is content: the crum (hash) of a passage is its identity. Replication dedupes for free — two servers holding the same content agree it's the same content, no authority required.
BLAKE3 content addressing throughout; per-passage authorship with
Ed25519 signatures (who wrote what, character by character); server
identity in every address (domain, or a key-derived
ns- name — so even a server with no domain has an
unforgeable identity); and the whole provenance record can be
anchored into Bitcoin via OpenTimestamps, making
"this passage existed at this time" checkable without
trusting anyone's server. Gold designed verifiable identity;
Xudanu adds cryptographic teeth and a timestamp floor.
Users don't read documents uniformly — they work in one region for an hour. The machine shouldn't treat all bytes equally.
Uniform access cost everywhere: the passage you're editing is as expensive as the archive nobody's opened in a year.
Self-adjusting tree behavior: frequently-touched nodes migrate toward the root, so the working set enjoys amortized near-constant access while cold content stays deep. The tree learns the hot path from use itself.
Splay-style exposure exists at the Edition level; the modern addition is the reuse-detection index (n-gram + MinHash tiers), which lazily builds exactly where editing happens. Partial parity — an optimization with headroom.
Undo, audit, versioning, recovery — four features in most systems, one mechanism in this one.
Rewrite files in place and bolt on separate mechanisms: an undo stack, an audit log, a backup regime, a version-control side-system. Each drifts from the truth; none compose.
Write-once storage: chunks are immutable, editions are trees of chunk references, history is the sequence of references — not copies. Undo, audit, and versioning become views of the same structure.
The same write-once discipline, extended: a WAL between checkpoints, a tamper-evident chained security log, manifest history with hash-chained slots, garbage collection that preserves referenced history, forensic checks over the chain (FR-63), and the OTS anchoring from Problem 10. History, audit, backup, and timestamp-proof are one mechanism with four names.
None of the twelve solutions is "do the naive thing, faster." Each one changes the structure so the work disappears: summaries make comparison unnecessary, hierarchy makes scanning unnecessary, references make copying unnecessary, registration makes searching unnecessary, immutability makes repair unnecessary. Gold never fixes up data it can make structurally unnecessary. That is the through-line, and it's why the solutions still read as modern forty years later.
The demo lives at xudanu.com — transclusion, typed links, provenance, and versioned addressing in a browser. The source is Apache-2.0 at github.com/jonesd/xudanu. For the condensed version of this document, see the Coles Notes; for the full modern spec, FR-64 in the repository docs.