Vault-LD
Brain spec
First committed by dfreytag
Published in The-Knowledge-Graph-Guys/vault-ld. Publisher page
246 GitHub stars
What it is
Vault-LD: an open spec for Markdown vaults as linked data. YAML-LD frontmatter + a shared `@context` = an RDF knowledge graph. Prose for humans and LLMs, triples for machines.
AutoGineer overview, from the brain’s own files
Vault-LD provides a standardized way to bridge human-readable Markdown notes with machine-readable RDF knowledge graphs. By utilizing YAML-LD in the frontmatter of Markdown files and a shared context, it allows for a seamless roundtrip between prose and structured data. This enables AI agents and other systems to reason over a vault's contents as a graph while maintaining a format that remains easy for humans to edit and version control in Git. The specification includes reference tools for exporting vaults to RDF and ingesting RDF into vault structures, ensuring full fidelity in both directions.
How it works
Memory model: LLM wiki
AutoGineer overview, from the brain’s own files
Vault-LD organizes information in a folder-based structure of Markdown files. Each file uses YAML-LD frontmatter to define its properties and relationships, which are resolved against a shared context file. The system uses reference scripts to convert these Markdown files into RDF triples (schema and data layers) and vice versa, allowing the vault to function as both a human-editable wiki and a structured knowledge graph.
| Folder | Role | Files |
|---|---|---|
| .github | agent-config | 11 |
| docs | docs | 39 |
| images | attachments | 3 |
| scripts | scripts | 7 |
| Vault-LD Example | folder | 14 |
The agent writes
- .github/ evidence: AGENTS.md
Requirements
Works with: OpenAI Codex, GitHub Copilot, Cursor, Kiro, OpenCode, Amp, Cline, JetBrains Junie, Devin (ex-Windsurf)
Set it up
Clone the repository at the commit AutoGineer scanned:
git clone https://github.com/The-Knowledge-Graph-Guys/vault-ld.git && cd vault-ld && git checkout 025e71be8d810e387dc451c920e05472d1c47170Point Claude Code at AGENTS.md: it does not read that file on its own.
cd vault-ld && claude
AutoGineer scanned these files at commit 025e71b.
First committed by dfreytag. Published in The-Knowledge-Graph-Guys/vault-ld. Licence: Apache License 2.0.
Usage
AutoGineer overview, from the brain’s own files
- Defining business semantics and ontologies within a Markdown-based wiki.
- Enabling AI agents to perform graph-based reasoning over local note collections.
- Synchronizing external RDF data sources with a human-friendly Markdown vault.
- Maintaining a version-controlled knowledge base that is simultaneously readable by humans and machines.
- “List all recipes in the vault that require chickpeas.”
- The agent queries the RDF graph derived from the Markdown frontmatter to identify all notes with type 'Recipe' and 'requiresIngredient' set to 'Chickpeas'.
- “Update the prep time for the Hummus recipe to 30 minutes.”
- The agent modifies the 'prepTimeMinutes' field in the YAML frontmatter of the Hummus.md file, ensuring the change is reflected in both the prose and the underlying knowledge graph.
Documentation by dfreytag
<div align="center">
<picture> <source media="(prefers-color-scheme: dark)" srcset="images/logo-dark.svg"> <img src="images/logo-light.svg" alt="Vault-LD logo" width="140"> </picture>
Vault-LD
An open format for knowledge that two very different readers can share at once:<br>a person editing notes, and a machine reasoning over a graph.
Image: CI Image: Release Image: Docs Image: License Image: GitHub stars Image: Issues Image: Contributions welcome
Specification · Docs site · Example vault · Contributing
</div>
RDF in the Vault
Karpathy's "LLM wiki" idea points at something real: a directory of plain Markdown notes is one of the most powerful ways to work with LLMs. It is readable by you, chunkable by a model, and diffable in git, with no database required. The YAML frontmatter at the centre of this approach carries a hidden pattern: it can map onto YAML-LD (JSON-LD with a YAML serialization). Resolve that frontmatter through one shared context and your notes stop being tagged text. They become linked data.
This matters for anyone already invested in RDF-based ontologies. Your business semantics (your classes, properties, and controlled vocabularies) drop straight into the wiki your team already uses, much as schema.org gave web pages a shared vocabulary without asking authors to leave HTML. The benefit runs both ways: business semantics arrive inside the wiki, and the ontology gains a human-friendly surface, where classes and properties can be read and edited as notes rather than as raw Turtle.
Both directions work because of the roundtrip. Since the frontmatter is YAML-LD, a note and an ontology definition are the same kind of object, and either can be projected losslessly to RDF and back. An ontology can enter as Turtle, be edited as Markdown, and leave as Turtle again, or the reverse, with no canonical "real" form privileged over the other.
What's here
SPEC.md: the normative reference. It defines how frontmatter becomes a knowledge graph (§4) and how any RDF graph round-trips through the vault format with full fidelity (§5), along with terminology, directory structure, conformance criteria, and a compatibility profile for lifting OKF-style Markdown bundles into linked data (Appendix B).
Vault-LD Example/: a complete, copyable vault that demonstrates every rule in the spec. It holds one ontology (Culinary), one controlled vocabulary (Difficulty Levels), and one instance (hummus). Its rootcontext.jsonldcomposes the Culinary ontology's own context, showing how multiple contexts compose (SPEC §4.2).scripts/vault_to_rdf.py: a reference exporter that projects a vault to RDF — see Exporting to RDF below.scripts/rdf_to_vault.py: the reference ingester running the other way, RDF → vault — see Ingesting RDF below. Together the two tools close the roundtrip.
Exporting to RDF
The repo ships scripts/vault_to_rdf.py, a small Python tool that reads a vault and emits Turtle, split by layer into two files:
schema.ttl— the schema layer (classes, properties, concepts), each ontology in its own namespace (cul:,diff:, …),data.ttl— the instance layer (typed notes) under the vault base, each IRI minted from the note's vault-relative path.
It classifies each note by its folder, mints each subject under the @base of its governing context (schema notes from their file name, instances from their vault-relative path — SPEC §4.5), resolves frontmatter through the vault's composed @context, and flags anything it can't map rather than dropping it. Export the example vault to build/ in one line:
pip install rdflib pyyaml && python scripts/vault_to_rdf.py "Vault-LD Example" --out-dir buildSee EXPORT.md for the full usage guide, flags, and how layer classification and context composition work.
Ingesting RDF
The reverse direction, scripts/rdf_to_vault.py, projects any RDF graph into the vault format (SPEC §5.5) — one note per subject, hierarchy in frontmatter, IRI objects as wiki links. Point it at an existing vault and it updates notes in place, preserving bodies, tags:, and folder structure, and leaving unchanged notes untouched; point it at an empty directory and it builds the whole vault, synthesizing a composed context.jsonld when none exists. Predicates the context doesn't map get a short name coined and added to the right context document; anything the format can't express (blank nodes, language tags) is flagged, never dropped (SPEC §5.6). Import a foreign ontology in one line:
python scripts/rdf_to_vault.py MyVault foreign-ontology.ttlSee INGEST.md for the full usage guide and the fidelity rules. Because the two tools are inverses, vault → RDF → vault is a no-op and RDF → vault → RDF is graph-isomorphic.
A taste
Read this note:
---
type: "[[Recipe]]"
requiresIngredient: "[[Chickpeas]]"
prepTimeMinutes: 25
---
# Hummus
A smooth purée of chickpeas, tahini, lemon, and garlic.You have just read these triples:
@prefix : <https://example.org/> .
:Hummus a :Recipe ;
:requiresIngredient :Chickpeas ;
:prepTimeMinutes 25 .The frontmatter is the graph; the body is prose for human and machine readers. One shared context.jsonld supplies the IRIs, datatypes, and prefixes, so authors write short, clean names while the semantics are supplied for them. See the Vault-LD Specification for the full rules.
Contributing
Vault-LD is an open standard and contributions are welcome: see CONTRIBUTING.md. Spec changes start life as an issue using the Spec change proposal template; fixes to the reference tools, example vault, and docs can go straight to a pull request.
License
<div align="center">
If Vault-LD is useful to you, a ⭐ on GitHub helps others find it.
</div>
scripts/EXPORT.md
Exporting a Vault to RDF — vault_to_rdf.py
vault_to_rdf.py projects a Vault-LD vault (a directory of Markdown notes, per
the SPEC) into RDF. It reads each note's YAML frontmatter as YAML-LD
through the vault's composed @context, then emits two Turtle files split by
layer:
| File | Holds | Namespaces |
|---|---|---|
schema.ttl | the schema layer — classes, properties, ontologies, concept schemes, concepts | one per ontology/vocabulary, e.g. cul: https://example.org/culinary#, diff: https://example.org/difficulty# |
data.ttl | the instance layer — typed notes (recipes, ingredients, …) | the vault base (the root context's @base), e.g. https://example.org/ |
Each ontology/vocabulary lives in its own namespace, declared as the @base
in that ontology's context. Cross-references between layers are preserved: an
instance's type and object properties point across into the relevant ontology
namespace, so the two files together are one graph.
Requirements
pip install rdflib pyyamlUsage
python scripts/vault_to_rdf.py <vault> [--context PATH] [--out-dir DIR] [--schema-ns IRI] [--data-ns IRI] [--source]Run against the bundled example vault:
python scripts/vault_to_rdf.py "Vault-LD Example" --out-dir build| Flag | Default | Meaning |
|---|---|---|
vault (positional) | — | path to the vault root directory |
--context | <vault>/context.jsonld | the root context document |
--out-dir | . | where to write schema.ttl and data.ttl |
--data-ns | the root context's @base | explicit vault-root base for instance subjects; replaces the root @base only — a data folder's own context.jsonld still governs its subtree |
--schema-ns | https://example.org/schema/ | fallback base for a schema folder whose context declares no @base |
--source | off | emit the vld:path placement triples that make the export a roundtrip face; without it the output is a lean, read-only query artifact (see below) |
How it decides what goes where
Layer is determined by folder location (SPEC §3, §5.1), not by guessing
from @type:
notes under
Ontologies/andVocabularies/→ schema layer,everything else with frontmatter → data layer.
The acceptable-type list is used only as an error bound: given a note's
location the tool knows what kind of resource it ought to be
(Classes/ → owl:Class, Properties/ → an OWL property type, the ontology
file → owl:Ontology, a vocabulary scheme → skos:ConceptScheme, the rest of a
vocabulary → skos:Concept). If a note's @type isn't an expected type for its
folder, the tool warns rather than silently mis-modelling it.
A subject's IRI is minted from its location, against the @base of its
governing context — per the scoped-base rule (SPEC §4.2, §4.5):
schema notes use the file name alone, resolved against the
@basedeclared in their own ontology's/vocabulary'scontext.jsonld(read in isolation, so each keeps a scoped base):Recipe.mdunderOntologies/Culinary/→cul:Recipe,Beginner.mdunderVocabularies/DifficultyLevels/→diff:Beginner. TheClasses/andProperties/folders — and any organisational nesting inside them (SPEC §5.2) — never enter the IRI;instance notes use the file name alone too, against the
@baseof the governing context (the nearestcontext.jsonldabove them, usually the vault root):Recipes/hummus.md→<https://example.org/hummus>— the folder path never enters the IRI;characters not legal in an IRI (spaces, most commonly) are percent-encoded (SPEC §4.5);
an explicit
idin frontmatter overrides name-based minting: the value is a full absolute IRI (http(s)://…), used verbatim — this is how two same-named files avoid minting the same IRI.
The default export is a query artifact, not a roundtrip face. The common
use of a .ttl export is as a read-only engine to query against, so by
default the output carries the graph's content and nothing else.
--source makes the export a roundtrip face: it adds a
vld:path "<path>.md" triple — Vault-LD's own string-valued property, in the
vld: namespace https://github.com/The-Knowledge-Graph-Guys/vault-ld# —
for every note whose location the graph
can't otherwise reconstruct (SPEC §5.4 step 7) — most notes, since identity
carries no location: every pinned note, every instance not sitting directly in
its governing context's folder, and every schema note away from the flat
placement of §5.1 — so ingest can restore every file 1:1. The graph content
is identical either way; --source only adds the placement bookkeeping. A
default (sourceless) export cannot restore the vault's folder structure on
ingest, which is exactly why it should stay read-only.
Folder nesting is never read as hierarchy (SPEC §5.2): subClassOf,
broader, and topConceptOf come from frontmatter and nowhere else. Nesting
a schema file is purely organisational; if its placement doesn't match what
its declared hierarchy implies, the true path simply travels as
vld:path so the location round-trips.
If a schema folder's context declares no @base, the tool falls back to
--schema-ns + the ontology name.
How the context is resolved
The tool follows JSON-LD context composition (SPEC §4.2). The root
context.jsonld may set its @context to an array:
{
"@context": [
{ "@base": "https://example.org/", "label": "rdfs:label", "...": "..." },
"Ontologies/Culinary/context.jsonld"
]
}object entries are merged inline;
string entries are references to other context files, resolved relative to the document that names them, and merged recursively;
entries apply left-to-right, so later entries override earlier ones.
This lets each ontology ship its own self-contained context.jsonld (its domain
vocabulary and its namespace, via @base) while the root simply lists the
contexts it composes. A referenced context's @base scopes only its own
ontology's subjects — it does not override the root's base in the merged context.
Remote (http(s)://) references are reported, not fetched.
Warnings
The tool prints warnings to stderr instead of dropping anything silently (SPEC §5.6). You will see one when:
a frontmatter field is not defined in the context (it is skipped — add it to the context to make it first-class);
a schema-folder note carries an unexpected
@typefor its location;a wiki link is dangling (its target note isn't found — the IRI is still minted, under the vault base);
an explicit
idis not an absolute IRI (non-conforming, SPEC §4.5 — the value must be a fullhttp(s)://…IRI; a relative value is flattened against the governing@baseas recovery);a referenced context file is missing or remote;
two participating notes share a file name, making bare wiki links to that name ambiguous (SPEC §4.4.1) — or mint the same IRI, silently merging into one subject;
two composed contexts define the same term or prefix differently — the later definition wins (SPEC §4.2); an identical re-declaration stays silent.
Host-editor keys (tags, aliases, cssclasses) are skipped silently
while unmapped: they are affordances of the editing surface, not unmapped
constructs (SPEC §4.3). A deployment that maps one in the context has
promoted it, and it exports like any other term. Wiki links are resolved per the SPEC §4.4.1 grammar: aliases
([[name|shown]]) and fragments ([[name#Heading]]) are stripped, and a
path-qualified link ([[path/to/name]]) selects among same-named notes by
matching its path against each note's vault-relative path (right-aligned on
segment boundaries, the way Obsidian's shortest-sufficient-path links work).
Keyword aliases declared in the context (type: for @type, id: for
@id; SPEC §4.3) are honoured on input — the aliased and quoted spellings
read identically.
Example output
For the bundled example vault, the default (query-only) data.ttl is:
@prefix cul: <https://example.org/culinary#> .
@prefix data: <https://example.org/> .
@prefix diff: <https://example.org/difficulty#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
data:hummus a cul:Recipe ;
cul:difficulty diff:Beginner ;
cul:prepTimeMinutes 25 ;
cul:requiresIngredient data:Chickpeas .
data:Chickpeas a cul:Ingredient ;
rdfs:label "Chickpeas" .With --source, each instance additionally carries its shelf location —
data:hummus gains vld:path "Recipes/hummus.md", data:Chickpeas
gains vld:path "Ingredients/Chickpeas.md" — which is what lets an
ingest rebuild the folder structure.
The data: prefix is bound to the vault base for condensed output — local
names are file stems (SPEC §4.5), so every unpinned instance compacts to
data:name.
Note This is the export direction (Vault → RDF), which assumes Markdown is the source of truth. The generated
.ttlfiles are read-only artifacts: edit the notes and regenerate, don't patch the Turtle (SPEC §5.4). For the opposite direction — RDF → Vault, when Turtle is the source of truth or when importing foreign RDF — seerdf_to_vault.py.
85 files · 5 not served here
- .github/.release-please-manifest.json
- .github/CHANGELOG.md
- .github/CODEOWNERS
- .github/ISSUE_TEMPLATE/bug-report.md
- .github/ISSUE_TEMPLATE/spec-change.md
- .github/PULL_REQUEST_TEMPLATE.md
- .github/release-please-config.json
- .github/workflows/ci.yml
- .github/workflows/pages.yml
- .github/workflows/pr-check.yml
- .github/workflows/release.yml
- .gitignore
- AGENTS.md
- ATTRIBUTION.md
- CODE_OF_CONDUCT.md
- CONTRIBUTING.md
- HISTORY.md
- LICENSE
- Makefile
- README.md
- SECURITY.md
- SPEC.md
- Vault-LD Example/Ingredients/Chickpeas.md
- Vault-LD Example/Ontologies/Culinary/Classes/CreativeWork.md
- Vault-LD Example/Ontologies/Culinary/Classes/Ingredient.md
- Vault-LD Example/Ontologies/Culinary/Classes/Recipe.md
- Vault-LD Example/Ontologies/Culinary/Culinary.md
- Vault-LD Example/Ontologies/Culinary/Properties/prepTimeMinutes.md
- Vault-LD Example/Ontologies/Culinary/Properties/requiresIngredient.md
- Vault-LD Example/Ontologies/Culinary/context.jsonld
- Vault-LD Example/Recipes/hummus.md
- Vault-LD Example/Vocabularies/DifficultyLevels/Beginner.md
- Vault-LD Example/Vocabularies/DifficultyLevels/DifficultyLevels.md
- Vault-LD Example/Vocabularies/DifficultyLevels/NoCook.md
- Vault-LD Example/Vocabularies/DifficultyLevels/context.jsonld
- Vault-LD Example/context.jsonld
- docs/assemble.mjs
- docs/docusaurus.config.js
- docs/package-lock.json
- docs/package.json
- docs/sidebars.js
- docs/src/css/custom.css
- docs/src/theme/NotFound/Content/index.js
- docs/static/CNAME
- docs/static/img/favicon.svg
- docs/static/img/social-card.png (not served here)
- docs/static/robots.txt
- docs/versioned_docs/version-0.3.0/CONTRIBUTING.md
- docs/versioned_docs/version-0.3.0/HISTORY.md
- docs/versioned_docs/version-0.3.0/SECURITY.md
- docs/versioned_docs/version-0.3.0/SPEC.md
- docs/versioned_docs/version-0.3.0/images/Vault-LD.png (not served here)
- docs/versioned_docs/version-0.3.0/images/logo-dark.svg
- docs/versioned_docs/version-0.3.0/images/logo-light.svg
- docs/versioned_docs/version-0.3.0/index.md
- docs/versioned_docs/version-0.4.0/CONTRIBUTING.md
- docs/versioned_docs/version-0.4.0/HISTORY.md
- docs/versioned_docs/version-0.4.0/SECURITY.md
- docs/versioned_docs/version-0.4.0/SPEC.md
- docs/versioned_docs/version-0.4.0/images/Vault-LD.png (not served here)
- docs/versioned_docs/version-0.4.0/images/logo-dark.svg
- docs/versioned_docs/version-0.4.0/images/logo-light.svg
- docs/versioned_docs/version-0.4.0/index.md
- docs/versioned_docs/version-0.5.0/CONTRIBUTING.md
- docs/versioned_docs/version-0.5.0/HISTORY.md
- docs/versioned_docs/version-0.5.0/SECURITY.md
- docs/versioned_docs/version-0.5.0/SPEC.md
- docs/versioned_docs/version-0.5.0/images/Vault-LD.png (not served here)
- docs/versioned_docs/version-0.5.0/images/logo-dark.svg
- docs/versioned_docs/version-0.5.0/images/logo-light.svg
- docs/versioned_docs/version-0.5.0/index.md
- docs/versioned_sidebars/version-0.3.0-sidebars.json
- docs/versioned_sidebars/version-0.4.0-sidebars.json
- docs/versioned_sidebars/version-0.5.0-sidebars.json
- docs/versions.json
- images/Vault-LD.png (not served here)
- images/logo-dark.svg
- images/logo-light.svg
- scripts/EXPORT.md
- scripts/INGEST.md
- scripts/compare_builds.py
- scripts/rdf_to_vault.py
- scripts/requirements.txt
- scripts/test_security.py
- scripts/vault_to_rdf.py
Advantages
- Enables lossless roundtripping between Markdown notes and RDF graphs. evidence: README.md
- Allows business semantics to be edited as human-friendly Markdown rather than raw Turtle. evidence: README.md
- Supports composition of multiple contexts for complex knowledge modeling. evidence: README.md
Limitations
- Requires adherence to specific YAML-LD frontmatter and context structures. evidence: README.md
- Certain RDF features like blank nodes or language tags may require specific handling or flagging. evidence: README.md
Security
Risk level: Low risk
This brain runs
Agent instructions that run or fetch something (3)
AGENTS.md:This is the repo's institutional memory, and it is load-bearing: contributors are expected to point their own agent at HISTORY.md (plus `git log`) to produce commits and PRs that match this repo's standards.AGENTS.md:**Security guarantees are pinned as tests** (`scripts/test_security.py`, run via `make test` in CI).AGENTS.md:**`make test` and `make roundtrip` must be green** before proposing a change.
- Low risk: network-instructions (Makefile:38)
Attribution and licence
- First committed by
dfreytag
- Publisher
- The Knowledge Graph Guys (Community)
- Source
- The-Knowledge-Graph-Guys/vault-ld · commit 025e71b
- Licence
- Apache License 2.0 (files served here)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Contributors
- C-HORNB, dfreytag, roelofsmn, tonyseale
AutoGineer is not affiliated with or endorsed by dfreytag or The Knowledge Graph Guys.
Install from source
5 of its files are not served here, so there is no ZIP. The-Knowledge-Graph-Guys/vault-ld at 025e71b