Typed Graph Schema (TGS) 0.1

Files: SPEC.md · tgs.schema.json · namespace.json · conformance examples
Version 0.1 (published 2026-10-06; immutable)
This version https://volland.github.io/obsigraph/spec/tgs/v0.1/
JSON Schema https://volland.github.io/obsigraph/spec/tgs/v0.1/tgs.schema.json
Namespace https://volland.github.io/obsigraph/ns/tgs# (prefix tgs:)
Editor Volodymyr Pavlyshyn
Source spec/tgs/ in github.com/Volland/obsigraph
License Text: CC BY 4.0. JSON Schema and examples: MIT.

TGS describes the types of a knowledge graph kept as markdown notes: what properties a note of a type has, which typed links it may have to which other types, and what properties those links carry. A schema is ordinary YAML frontmatter in ordinary notes, so people can write and review it by hand, and it maps to and from W3C SHACL so the same ontology can be exchanged with RDF tools.

TGS is independent of any one application. Typed Graph (an Obsidian plugin and CLI) is the reference implementation.

1. Conventions

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be read as described in RFC 2119 when they appear in capitals.

2. Data model

TGS types a labelled property graph built from notes:

3. Schema notes

A schema note is a markdown note located directly in the schema folder. The schema folder is a reader setting whose default is Types/. Notes in subfolders of the schema folder, and notes elsewhere in the vault, MUST NOT be read as schema notes.

A schema note's frontmatter MAY contain these keys, in any combination:

Key Value Declares
schema type declaration (section 4) the type named by the note's title
schemas mapping: type name → type declaration any number of types
edgeTypes mapping: edge type name → edge type declaration (section 6) edge types
prefixes mapping: prefix → IRI CURIE prefixes for the whole vault (section 7)
tgs version string, e.g. "0.1" the TGS version the note is written for (section 12)

A schema note with none of schema, schemas, edgeTypes and prefixes declares an empty type named by its title. All other frontmatter keys belong to the note itself and readers MUST ignore them. The body of a schema note is free text, except that the body of a note with schema is the template of that type (section 8).

One note can therefore describe a single type:

# Types/Person.md
---
schema:
  properties:
    name: text
    email: {kind: text, required: true}
  edges: {worksAt: Company, knows: Person}
---
## Notes

or a whole subsystem:

# Types/People and Orgs.md
---
schemas:
  Person:
    properties: {name: text, email: {kind: text, required: true}}
    edges: {worksAt: Company, knows: Person}
  Company:
    uri: schema:Organization
    properties: {name: text, founded: date}
edgeTypes:
  worksAt:
    from: Person
    to: Company
    properties: {since: date, role: {kind: text, values: [engineer, manager]}}
---
How people and organisations relate.

3.1 Duplicates

When the same type name, edge type name or prefix is declared more than once, readers MUST use the declaration from the schema note whose path sorts first (by code point) and MUST report a duplicate-declaration naming both notes. A prefix declared twice with the same IRI is not a duplicate.

4. Type declarations

A type declaration is a mapping with these optional keys:

Key Value Meaning
uri IRI or CURIE identifier of the type; default: base IRI + type name
properties section 5 frontmatter properties of notes of this type
edges section 5.3 allowed outgoing edges; absent means any edge is allowed
template link template note for new notes of this type (section 8)
visualization mapping display hints (section 11); style is an alias

An empty mapping ({}) or a null value declares a type with no constraints.

5. Properties and edges

5.1 Property declarations

properties is either a mapping from property name to a property declaration, or a list whose items are a property name (a text property) or a mapping with a name key. A property declaration is either a kind shorthand (born: date) or a mapping:

Key Value Default Meaning
kind one of the kinds below text value type
required boolean false the note must have a non-empty value
many boolean false the value is a list (always true for kind list)
values list of scalars none allowed values (an enumeration)
default any YAML value none value written into new notes
uri IRI or CURIE base IRI + name identifier of the property

A null declaration (name:) is a text property. Within one type, the first declaration of a property name wins.

5.2 Kinds

Kind Values Obsidian property type XSD datatype
text string Text xsd:string
number number Number xsd:decimal
boolean true / false Checkbox xsd:boolean
date YYYY-MM-DD Date xsd:date
datetime ISO 8601 date and time Date & time xsd:dateTime
link a link to a note or a URL Text (link) none; an IRI
list list of strings List xsd:string, many

An unknown kind MUST be read as text and reported as unknown-kind. TGS 0.1 does not require readers to check that values match their kind.

5.3 Edges of a type

edges lists the edge types a note of this type may have as outgoing edges. Two forms are allowed.

The list form names edge types, each accepting any target: edges: [knows, worksAt].

The map form maps each edge type to its target type, or to a list of target types, or to null (any target), or to a mapping:

Key Value Default Meaning
target type name or list any allowed target types
many boolean true more than one edge of this type is allowed
required boolean false at least one edge of this type is required
edges:
  worksAt: Company                          # one target type, many, optional
  knows: [Person, Bot]                      # several target types
  mentor: {target: Person, many: false, required: true}
  likes:                                    # any target

Properties default to a single value and edges default to many, because repeated edges of one type are normal (knows) and frontmatter values usually are not. An empty list or mapping (edges: []) allows no outgoing edges.

6. Edge type declarations

An edge type declaration describes an edge type everywhere it is used:

Key Value Meaning
from type name or list allowed source types; absent means any
to type name or list allowed target types; absent means any
properties as in 5.1 properties written on the edge
uri IRI or CURIE identifier of the edge type (an RDF predicate); default: base IRI + name
visualization mapping display hints for edges of this type (section 11); style is an alias

Undeclared edge properties are allowed. When both a type's edges entry and the edge type declare targets, the type's entry wins for sources of that type.

7. Identifiers

Every type, property and edge type has an IRI, so that schemas can be exchanged.

8. Templates

A reader that creates notes from types MUST build the new note's frontmatter from type: <name> and every declared default, and MUST take the body from the first of:

  1. the note that the type's template link points to, without its frontmatter;
  2. the body of the schema note, when the type is declared with schema, if that body is not empty;
  3. a template generated from the schema.

A template link is a wikilink ([[Templates/Person]], resolved as the host resolves links), a markdown link ([Person](../Templates/Person.md), relative to the schema note), or a vault-relative path (Templates/Person, .md optional).

A generated template SHOULD write a key for every declared property (its default, [] for many, empty otherwise) and one placeholder per declared edge that does not itself create an edge, so that a new note is complete but creates no links until it is filled in. The reference implementation writes ## Notes followed by ## Relations with one - <edgeType>:: line per edge.

9. Validation

Validation is advisory. A validator MUST report diagnostics and MUST NOT remove notes or edges from the graph. Stub targets and targets without labels are never reported as having the wrong type.

For a node with labels L1, L2, …, the effective schema merges the schemas of its labels in order: the first declaration of a property wins; edge rules are unioned per edge type, with targets unioned (any target if one rule allows any), many if any rule allows many and required if any rule requires it; edges is unrestricted only if no label declares it.

Diagnostic Reported when
invalid-declaration a key has a value of the wrong shape (e.g. edges: 5)
unknown-kind a property declares an unsupported kind
duplicate-declaration section 3.1
unknown-prefix section 7
unsupported-version section 12
unknown-key section 12
missing-property a required property is absent, null or empty
value-not-allowed a property value, or an item of a list value, is not in values
unexpected-list a property that is not many holds a list
edge-not-allowed an outgoing edge type is not in the effective edges
missing-edge a required edge has no edge of its type
too-many-edges an edge that is not many has more than one edge of its type
wrong-target-type the target has labels and none is an allowed target type
wrong-source-type the source has labels and none is in the edge type's from
missing-edge-property a required edge property is absent from the edge
edge-value-not-allowed an edge property value is not in its values

Readers SHOULD report each diagnostic with the note path and, for edges, the line of the edge. Message text is not specified.

10. Multiple labels

A note with type: [Person, Employee] is checked against the merged schema of section 9. The style of a node is taken attribute by attribute from the first label that supplies it.

11. Visualization (informative)

visualization is a hint for graph displays and does not affect validation. The reference implementation reads color (CSS color), shape, icon (a Lucide icon name) and label (a property shown as the node label) for types, color and line (solid, dashed, dotted) for edge types, and an edges mapping inside a type's visualization that styles edge types. An edge type's own visualization wins over a type's visualization.edges. Other readers MAY ignore visualization or read further keys.

12. Versioning

A schema note MAY declare tgs: "<major>.<minor>". A reader implementing version M.N:

Minor versions only add optional keys. A published version never changes.

13. SHACL mapping

TGS maps to the W3C Shapes Constraint Language (SHACL) so that schemas can be exchanged with RDF tools. An exporter writes a schema set as SHACL in Turtle. An importer reads SHACL into TGS declarations.

13.1 Types and properties

Each type becomes a sh:NodeShape named base IRI + type name + Shape:

TGS SHACL
type Person sh:targetClass = the type's IRI; sh:name "Person"
property a property shape with sh:path = the property's IRI and sh:name = its name
kind text, number, boolean, date, datetime sh:datatype per section 5.2
kind link sh:nodeKind sh:IRI
kind list sh:datatype xsd:string and tgs:kind "list", no sh:maxCount
required: true sh:minCount 1
not many sh:maxCount 1
scalar default sh:defaultValue
list default tgs:default with the value as a JSON literal
values sh:in ( … )
declaration order sh:order 0, 1, …

13.2 Edges of a type

Each entry of a type's edges becomes a property shape on the type's shape:

TGS SHACL
edge type sh:path = the edge type's IRI; sh:name = its name
one target sh:class = the target type's IRI
several targets sh:or ( [ sh:class A ] [ sh:class B ] )
any target sh:nodeKind sh:IRI and tgs:edge true
required: true sh:minCount 1
many: false sh:maxCount 1
edges declared tgs:edgesClosed true on the node shape

sh:closed is not used, because it would also close frontmatter properties, which are open in TGS. tgs:edgesClosed records the allow-list without changing what SHACL engines validate.

13.3 Edge types

Each edge type becomes a sh:NodeShape named base IRI + name + EdgeShape that describes the edge as an RDF reification (rdf:Statement):

:worksAtEdgeShape
    a sh:NodeShape ;
    sh:name "worksAt" ;
    sh:property [ sh:path rdf:predicate ; sh:hasValue schema:worksFor ] ;
    sh:property [ sh:path rdf:subject ; sh:class :Person ] ;
    sh:property [ sh:path rdf:object ; sh:class schema:Organization ] ;
    sh:property [ sh:path :since ; sh:name "since" ; sh:datatype xsd:date ; sh:maxCount 1 ; sh:order 0 ] .

The rdf:predicate constraint identifies the edge type; rdf:subject carries from and rdf:object carries to (with sh:or for several types); the remaining property shapes are the edge's properties. The shape declares no target, so validators apply it to reified statements on request. This uses only SHACL 1.0 and the RDF reification vocabulary; a later TGS version may add RDF 1.2 reifiers.

13.4 Annotations

What SHACL cannot express is kept as annotations, which SHACL engines ignore:

Term On Value
tgs:note node shape path of the schema note that declared it
tgs:template node shape the template link
tgs:templateBody node shape the schema note body of a schema type
tgs:visualization node shape the visualization mapping as a JSON literal
tgs:edgesClosed node shape true when the type declares edges
tgs:edge property shape true for an edge entry without target types
tgs:kind property shape "list" for kind list
tgs:default property shape a non-scalar default as a JSON literal

13.5 Import

An importer MUST read:

An importer MUST report, and otherwise skip, every construct outside this subset, including sh:closed, sh:pattern, sh:minLength, sh:qualifiedValueShape, sh:sparql, sh:and, sh:xone, sh:not, property paths other than a single IRI, unknown datatypes (read as text), sh:maxCount greater than 1 (read as many) and node shapes without a target class. It MUST import the rest of each shape.

An importer that writes notes SHOULD write one note per type by default (or group types by tgs:note), MUST replace only the schema, schemas, edgeTypes and prefixes keys of an existing note, MUST NOT delete notes, and SHOULD refuse to rewrite a note that declares types missing from the import unless the user asks. A note that declares several types has no template body, so an importer that groups types into one note SHOULD report each tgs:templateBody it cannot keep.

13.6 Round trip

For schemas written in TGS, export followed by import MUST reproduce the same types, edge types, properties, edges, identifiers, templates and visualization. Exporting the result again yields the same RDF graph; the reference implementation produces byte-identical Turtle.

14. Conformance

Level Requirements
Reader sections 3 to 8 and 12: reads every key, applies defaults, expands identifiers, reports declaration diagnostics
Validator Reader, plus section 9
SHACL exchanger Validator, plus section 13

The examples/ folder next to this document contains conformance cases. Each case has a vault/ folder (schema folder Types/, base IRI urn:tgs:) and an expected.json with:

SHACL cases also have a shapes.ttl that an exporter's output must be isomorphic to.

15. JSON Schema

tgs.schema.json (JSON Schema draft 2020-12) validates the TGS keys of a schema note's frontmatter. It is strict about TGS 0.1 keys and leaves visualization and all non-TGS keys open. Notes declaring a newer minor version may not validate against it.

16. References