No description
  • TypeScript 60%
  • C# 24.5%
  • JavaScript 7.2%
  • PowerShell 4.2%
  • C++ 4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-07-10 16:10:25 +01:00
.voidbot Rename EpiphanyGraph to Norn 2026-05-25 13:51:36 +01:00
benchmarks Rename EpiphanyGraph to Norn 2026-05-25 13:51:36 +01:00
crates Serve Norn plugin ABI over CultNet 2026-07-10 11:44:19 +01:00
docs Rename EpiphanyGraph to Norn 2026-05-25 13:51:36 +01:00
notes Merge Rust solver into Norn 2026-05-25 22:02:48 +01:00
plugins Own nested graph fixture in Norn 2026-07-10 16:10:25 +01:00
schemas Publish Norn Eve plugin sidecar 2026-07-10 02:49:28 +01:00
scripts Serve Norn plugin ABI over CultNet 2026-07-10 11:44:19 +01:00
state Publish Norn Eve plugin sidecar 2026-07-10 02:49:28 +01:00
web/norn-viewer Clarify viewport-derived focus probe 2026-07-06 22:09:27 +01:00
.gitignore Merge Rust solver into Norn 2026-05-25 22:02:48 +01:00
AGENTS.md Merge Rust solver into Norn 2026-05-25 22:02:48 +01:00
Cargo.lock Serve Norn plugin ABI over CultNet 2026-07-10 11:44:19 +01:00
Cargo.toml Publish Norn Eve plugin sidecar 2026-07-10 02:49:28 +01:00
FRIENDLINESS_TUNER.md Rename EpiphanyGraph to Norn 2026-05-25 13:51:36 +01:00
Norn.csproj Rename EpiphanyGraph to Norn 2026-05-25 13:51:36 +01:00
package-lock.json Serve Norn plugin ABI over CultNet 2026-07-10 11:44:19 +01:00
package.json Serve Norn plugin ABI over CultNet 2026-07-10 11:44:19 +01:00
Program.cs Rename EpiphanyGraph to Norn 2026-05-25 13:51:36 +01:00
README.md Serve Norn plugin ABI over CultNet 2026-07-10 11:44:19 +01:00
RichSvgRenderer.cs Rename EpiphanyGraph to Norn 2026-05-25 13:51:36 +01:00
TUNING.md Prototype geometry-driven rich graph renderer 2026-04-25 09:09:30 +01:00

Norn

Rust hybrid Sugiyama / Kamada-Kawai 2D and 3D graph layout solver for Epiphany typed graph state, with React/WASM and C# windows onto the solver.

The solver crate lives in crates/norn-rs. That is Norn's body. The React viewer in web/norn-viewer is the live browser inspection package for clients and agents that want to render architecture, dataflow, and typed cross-links in a UI.

The .NET generator, MSAGL SVG renderer, and MCP server still exist as support tools for note exports, SDK/bridge work, and legacy inspection. They are windows onto the solver, not competing engines. Do not make consumers choose between truths like this repo is a sad little trade show booth.

Eve Plugin Sidecar

crates/norn-eve-plugin is Norn's runtime-independent Eve plugin boundary. Its default entry point advertises a CultNet RUDP operation endpoint carrying MessagePack Eve ABI requests. The sidecar owns graph validation, semantic projection, and solver-backed measurement. It does not own provider state or renderer projection. Pass --stdio only for local NDJSON debugging.

Owner-published discovery and conformance documents live under plugins/ and the graph document/layout schemas live under schemas/.

npm run witness:eve-plugin

Rust Solver

cd E:\Projects\Norn
cargo test

The crate is under crates/norn-rs and exports both a normal Rust library and a cdylib target for the WASM bridge. Solver algorithm work starts there.

React Viewer

cd E:\Projects\Norn\web\norn-viewer
npm install
npm run dev
import { NornViewer } from "@gamecult/norn-viewer";
import type { NornGraphsState } from "@gamecult/norn-viewer";

export function GraphScreen({ state }: { state: NornGraphsState }) {
  return (
    <NornViewer
      state={state}
      layoutMode="combined-force"
      motion={{ strength: 1.05, flow: 1.1, orbit: 0.85 }}
      performance="fast"
    />
  );
}

The public viewer API is intentionally coarse:

  • layoutMode: layered, stress, force, or combined-force
  • motion: viewer-owned force tuning for Rust-solver combined-force layouts
  • performance: quality, balanced, fast, or explicit simulationBudgetMs realtime knobs
  • selection, focusSelection, expandedNode, and callbacks for app integration
  • typed state and event payloads

What It Does

The support generator:

  • scans markdown notes under a vault folder
  • resolves Obsidian wikilinks and regular markdown note links
  • optionally starts from an entry note and keeps only reachable notes
  • classifies notes into source-tree and control-flow graph families
  • lays out each family as its own graph partition
  • reports cross-links that still jump between those families
  • runs Microsoft Automatic Graph Layout
  • writes:
    • manifest.json
    • source-tree.svg
    • source-tree.msagl.svg
    • source-tree.rich.svg
    • source-tree.json
    • control-flow.svg
    • control-flow.msagl.svg
    • control-flow.rich.svg
    • control-flow.json
    • cross-links.json
    • warnings.txt

CLI Usage

Run from source:

dotnet run --project . -- `
  --vault-root "E:\Projects\Aetheria-Economy\Aetheria\Source Tree Map" `
  --entry-note "Source Tree Map" `
  --renderer rich `
  --output-dir ".\out\source-tree-map"

After packing as a .NET tool:

norn `
  --vault-root "E:\Projects\Aetheria-Economy\Aetheria\Source Tree Map" `
  --entry-note "Source Tree Map" `
  --renderer rich `
  --output-dir ".\out\source-tree-map"

MCP Usage

The MCP server exposes one tool:

  • generate_obsidian_graph_layout

Run From Source

Use this in an MCP client config when you want to launch the server straight from the repo:

{
  "mcpServers": {
    "norn": {
      "command": "dotnet",
      "args": [
        "run",
        "--project",
        "E:\\Projects\\Norn\\Norn.csproj",
        "--",
        "--mcp"
      ]
    }
  }
}

Pack As A .NET Tool

Build the package:

dotnet pack . -c Release -o .\nupkg

Install it from the local package output:

dotnet tool install --global --add-source .\nupkg Norn

Then point your MCP client at the installed command:

{
  "mcpServers": {
    "norn": {
      "command": "norn",
      "args": ["--mcp"]
    }
  }
}

MCP Tool Arguments

  • vaultRoot: path to the Obsidian vault folder
  • entryNote: optional entry note name or relative note path
  • outputDir: optional output directory, defaults to ./out
  • layout: sugiyama or mds
  • renderer: msagl or rich
  • includeUnreachable: keep notes that are not reachable from the entry note

MCP Tool Result

The tool writes manifest.json, primary partition SVGs, raw .msagl.svg variants, custom .rich.svg variants, partition JSON files, cross-links.json, and warnings.txt, then returns:

  • output paths for each partition
  • seed note IDs used for traversal
  • total note and edge counts
  • per-partition node and edge counts
  • warnings
  • the resolved entry note ID, if one was used

Graph Families

By default, notes whose basename contains Control Flow land in the control-flow partition, and everything else lands in source-tree.

You can override that with frontmatter on a note:

---
norn-family: control-flow
---

or:

---
norn-families:
  - source-tree
  - control-flow
---

Supported keys:

  • norn-family
  • norn-families
  • graph-family
  • graph-families

If your chosen entryNote is itself one of the explicit family roots, the generator also seeds traversal from any sibling family roots it can find.

  • Source Tree Map will also pull in Control Flow Map
  • Control Flow Map will also pull in Source Tree Map

That keeps structurally separated note graphs from vanishing just because they no longer link to each other directly.

Layout Modes

  • sugiyama: layered, better for hierarchy-heavy note trees
  • mds: force-ish spread, better when the graph is cross-linked chaos

mds is the closest MSAGL-native approximation to the Obsidian or Kamada-Kawai vibe. It is not a named Kamada-Kawai mode.

Renderer Modes

  • msagl: the stock MSAGL SVG, useful as the plain baseline
  • rich: a custom SVG renderer that consumes MSAGL graph geometry and semantic metadata, then draws something with more visual hierarchy and less beige despair

Support Tool: Norn Viewer Demo

The web-side viewer package lives under web/norn-viewer/.

It consumes the typed Norn graph state shape directly:

  • graphs.architecture
  • graphs.dataflow
  • graphs.links

and packages:

  • Rust hybrid solver layout through bundled WASM
  • zoom and pan
  • zoom-gated label and detail pop-in
  • node, edge, and code-ref inspection
  • typed cross-link browsing between architecture and dataflow
  • combined-force motion tuning for clients that want the lively overview

Run it locally with:

cd E:\Projects\Norn\web\norn-viewer
npm install
npm run dev

Notes

  • unresolved and ambiguous links are written to warnings.txt
  • node IDs in the partition .json files are relative note paths without .md
  • the current implementation estimates label sizes instead of using a platform text measurer, because this is a generator, not a cathedral
  • source-tree.svg and control-flow.svg are the primary outputs for the selected renderer
  • raw MSAGL output is always preserved as *.msagl.svg
  • the custom renderer currently emits *.rich.svg from MSAGL geometry, not from post-processing the stock SVG