Content Graph Architecture

Thinking-in-progress on how the vault’s relationship graph should be built, maintained, queried, and published. Captured 2026-03-09 after extended design discussion. See also: [Publishing Requirements](Publishing Requirements.md), [Diagram of Object Relationships](Diagram of Object Relationships.md).


Core principle: vault front matter is the authoritative data

All structural relationships (isIn, partOf, dependsOn) live in note front matter. Everything else — the published website’s related-items sections, the agent query index, Obsidian Dataview/Bases views — is derived from that data. No derived artifact is authoritative; all can be rebuilt from the vault.


Four consumers, four different needs

ConsumerWhat it needsMechanism
Quartz / web siteStatic markdown it can render; related-items baked into outputQuartz transformer reads front matter at build time, injects relationship sections
Agent / automationQueryable, traversable graph; fast lookupsindex.json built from vault front matter; vault files are too slow to query directly
Obsidian (authoring)Dynamic views while editingDataview queries or Bases in templates; local only, not published
n8n triage workflowsDependency graph for root-cause analysisSame index.json; walk dependsOn upward from a failing device

These are genuinely different problems. Trying to solve all four with one mechanism causes the design to feel intractable.


The build-index approach

A script (build-index.py or equivalent) walks the vault, reads all front matter, and emits a single index.json containing the full graph:

{
  "nodes": {
    "50-Devices/Home Network/UDM.md": {
      "title": "Unifi Dream Machine (UDM) Pro SE",
      "isA": "device",
      "isIn": "30-Areas/Duplex/Unit B/Network Shelves.md",
      "partOf": ["40-Systems/Home Network/index.md"],
      "dependsOn": ["50-Devices/Power/AC Power/UPS/Lab UPS.md"]
    },
    ...
  }
}
  • Vault is the source — index is rebuilt on demand, on commit, or by n8n trigger
  • Agents query the index, not raw markdown files
  • Quartz transformer can consume the same data at build time
  • Front matter link maintenance pain is real but contained to the source; the index is always rebuilt fresh so stale derived data is not a risk

Six traversal directions on the existing properties drive the related-items section for any note:

DirectionDerived fromExample
Up via isInnote’s own front matter”Located in: Network Shelves”
Down via isIninverse — all notes pointing here”Contains: UDM, Patch Panel, …”
Up via partOfnote’s own front matter”Part of: Home Network”
Down via partOfinverse — all notes pointing here”Members: UDM, Fiber Modem, …”
Out via dependsOnnote’s own front matter”Depends on: UPS, Fios”
In via dependsOninverse — all notes pointing here”Required by: n8n, Home Assistant, …”

Not every note has all six. The Quartz transformer should only render sections that have at least one entry.

See [Publishing Requirements](Publishing Requirements.md) for parent trail, child list, sibling, and ordered-member rendering details.


Discovery integration (planned, not started)

Unifi and Home Assistant can be sources of truth for devices they can see:

  • Unifi: MAC address, IP, switch port, AP association, hostname
  • Home Assistant: every integrated device and its state

Proposed flow: new device appears in Unifi → n8n webhook → create stub note in 50-Devices/ with known front matter pre-filled (title from hostname, isA: device, dependsOn from network topology if determinable).

What discovery tools cannot see — physical infrastructure, upstream services, partOf relationships — still requires human input, but that is a much smaller surface.


The connections question (open)

Physical connections (ethernet cables, power cords) are not currently modeled. The test for adding a property: would a person troubleshooting or an n8n workflow benefit from knowing this?

Ethernet connections — worth defining. If the UDM goes down, knowing which devices are on which port is actionable for triage. dependsOn captures the dependency but not the physical path.

Proposed simple form (to be refined):

connectedTo:
  - "[[50-Devices/Home Network/UDM.md]]"

Port details can live in the note body rather than front matter for now.

Power connections — not yet. dependsOn: UPS already captures the meaningful operational dependency. Physical cord tracking adds little until there is a managed PDU that can act on the information.


What is deferred

The following are parked pending more content being written “the hard way,” which will clarify what hurts most:

  • Auto-generating and maintaining related-items sections in note bodies
  • Deciding between inline injection vs sidecar files vs Quartz-only rendering
  • Breadcrumbs plugin investigation (configured but not yet fully understood)
  • Dataview backfill script for existing notes
  • build-index.py implementation
  • Ordered-member rendering (prev/next for circuits, irrigation zones, etc.)

Open questions

  1. Should related-items sections be injected into note bodies (requires maintenance), live in sidecar .ctx.md files, or only be rendered by Quartz at build time (never touch source files)?

  2. What triggers index rebuilds — git commit hook, n8n webhook on vault change, or on-demand script?

  3. Should connectedTo be added to the property model now, or wait until there are enough device notes to make the value clear?