Architecture
System Overview
Xanadu Gold is a Rust library that compiles to WebAssembly for browser and Node.js consumption. It implements the Xanadu hypertext model: a content-addressable document store built on a partially ordered trace history.
graph TB
subgraph "JavaScript / TypeScript"
APP["Client Application"]
APP --> WASM["xudanu.js<br/>(WASM glue)"]
end
subgraph "WASM Module (Rust)"
WASM --> D["WasmDagWood"]
WASM --> TV["WasmTraceView"]
WASM --> E["WasmEnt"]
WASM --> AS["WasmAssertionStore"]
D --> DW["DagWood<br/>(partial ordering)"]
TV --> DW
E --> DW
AS --> CS["AssertionStore<br/>(content layer)"]
AS --> DW
end
subgraph "Core Layer (src/ent/)"
DW --> BS["BranchStore"]
DW --> TP["TracePosition"]
CS --> AP["AssertionPayload<br/>(13 variants)"]
CS --> MV["Materialization<br/>(doc/node/span)"]
end
Module Dependencies
graph LR
trace["trace.rs<br/>TracePosition"]
branch["branch.rs<br/>BranchId, BranchStore"]
dagwood["dagwood.rs<br/>DagWood, TraceView"]
content["content.rs<br/>Assertions, Materialization"]
ent["ent.rs<br/>Ent (table manager)"]
wasm["wasm.rs<br/>WASM bindings"]
trace --> branch
dagwood --> branch
dagwood --> trace
content --> dagwood
content --> trace
content --> branch
ent --> dagwood
wasm --> dagwood
wasm --> content
wasm --> ent
Data Flow: Document Lifecycle
sequenceDiagram
participant JS as JavaScript
participant DW as DagWood
participant AS as AssertionStore
participant TV as TraceView
JS->>DW: new DagWood()
JS->>DW: root()
Note right of DW: Creates root branch<br/>position (1,1)
JS->>DW: new_position()
Note right of DW: Forks new branch<br/>returns position (3,3)
JS->>AS: add(position, CreateNode)
JS->>AS: add(position, CreateSpan)
JS->>AS: add(position, SetSpanText)
Note right of AS: Assertions stored<br/>at trace positions
JS->>DW: trace_view(position)
DW->>TV: Compute visibility snapshot
Note right of TV: Caches which branches<br/>and positions are visible
JS->>AS: materialize_document(view, 1)
AS->>TV: Filter visible assertions
AS->>JS: Return JSON object tree
Data Flow: Fork and Merge
gitGraph
commit id: "root (1,1)"
commit id: "trunk (2,3)"
branch forkA
commit id: "branch A (3,3)"
branch forkB
commit id: "branch B (4,3)"
checkout forkA
commit id: "extend A (3,4)"
checkout main
merge forkA id: "merge (5,3)"
merge forkB id: "extend merge"
The partial ordering after this sequence:
graph TD
R["root<br/>(1,1)"] --> T["trunk<br/>(2,3)"]
T --> A["branch A<br/>(3,3)"]
T --> B["branch B<br/>(4,3)"]
A --> C["extend A<br/>(3,4)"]
A --> M["merge<br/>(5,3)"]
B --> M
R -.->|is_le| A
R -.->|is_le| B
A -.->|is_le| C
A -.->|is_le| M
B -.->|is_le| M
B -.->|not ≤| C
C -.->|not ≤| B
AssertionPayload Variants
All 13 operations that modify document content:
graph TD
AP[AssertionPayload]
AP --> N["Node Operations"]
AP --> S["Span Operations"]
AP --> AN["Annotation Operations"]
N --> N1["CreateNode"]
N --> N2["AttachChild"]
N --> N3["DetachChild"]
N --> N4["DeleteNode"]
S --> S1["CreateSpan"]
S --> S2["SetSpanText"]
S --> S3["DeleteSpan"]
S --> S4["AttachSpanToNode"]
S --> S5["DetachSpanFromNode"]
AN --> A1["CreateAnnotation"]
AN --> A2["AttachAnnotationToNode"]
AN --> A3["AttachAnnotationToSpan"]
AN --> A4["DeleteAnnotation"]
Merge Semantics
When branches diverge and merge, the content layer follows these rules:
graph TD
M[Two branches merge] --> Q1{Same property?}
Q1 -->|No| COEXIST["Both visible<br/>(e.g. text + annotation)"]
Q1 -->|Yes| Q2{Same value?}
Q2 -->|Yes| COLLAPSE["Collapse to Single"]
Q2 -->|No| Q3{Is one a delete?}
Q3 -->|Yes| DELETE["Delete wins"]
Q3 -->|No| ALTS["AlternativeSet<br/>(all values preserved)"]
COEXIST --> RULE1["Rule M1: Compatible merge"]
COLLAPSE --> RULE2["Rule M3: Agreement collapse"]
DELETE --> RULE3["Rule M4: Delete vs modify"]
ALTS --> RULE4["Rule M2: Conflict → alternatives"]
Materialized Document Tree
graph TD
DOC["MaterializedDocument<br/>{doc_id, root?}"]
DOC --> ROOT["MaterializedNode<br/>{node_id, kind, children, spans, annotations}"]
ROOT --> CHILD["MaterializedNode[]<br/>(recursive children)"]
ROOT --> SPAN["MaterializedSpan[]<br/>{span_id, text, annotations}"]
ROOT --> ANN["MaterializedAnnotation[]<br/>{annotation_id, kind, payload}"]
SPAN --> TEXT["text: AlternativeSet"]
TEXT --> SINGLE["{ Single: 'hello' }"]
TEXT --> MULT["{ Alternatives: ['a', 'b'] }"]
SPAN --> SANN["MaterializedAnnotation[]"]
WASM Serialization Path
graph LR
R["Rust Struct"] -->|serde_json::<br/>to_string| JSON["JSON String"]
JSON -->|js_sys::<br/>JSON::parse| JS["JS Object"]
JS -->|return to<br/>caller| APP["Application"]
APP -->|payload JSON string| API["store.add()"]
API -->|serde_json::<br/>from_str| R2["AssertionPayload"]
This path avoids BigInt issues (u64 → regular JS Number) and produces standard JSON compatible with all network protocols.