Open format · v0.2 · Read + write, tested

Knowledge Graph
Language.

A plain-text file format for building knowledge graphs on the file system. Relationships are first-class citizens. No database. No graph engine. Just files.

projects/search-revamp.kgl
# projects/search-revamp.kgl

@Project Search Revamp
status: in-progress
deadline: 2025-08-01
#search #q3 #vector

[owned-by] → people/bob.kgl#Bob Chen
    since: 2024-09-01

[depends-on|replaces] → services/legacy-search.kgl
    sunset-date: 2025-08-01
    note: "vector index must be live before deprecation"

[staffed-by] → people/alice.kgl#Alice Nguyen
    role: lead engineer
    since: 2024-11-01

~> decisions/search-engine-choice.kgl  # background reading

Relationships are data too.

Existing formats optimise for data (JSON, CSV) or documents (Markdown, XML). Neither treats how things connect as structural. KGL is built around the idea that relationships deserve as much structure as the nodes they link.

Links as first-class syntax

Hard links () and soft links (~>) signal traversal intent directly in the file. An AI reading a KGL file knows immediately what to follow and what is background context.

@

Typed nodes

@Person, @Project, @Service. Types are semantic and machine-readable. A node can carry multiple types: @Person|Contractor.

[ ]

Named relationships

Every edge has a label and optional typed properties. [depends-on|replaces] with a sunset-date is a relationship that carries its own data.

fs/

File system as graph

Folders are namespaces. Files are nodes. No database, no server. Query with grep. Traverse with any file-reading tool. Version with git.

Syntax reference.

@Type Name
Node declaration. Capitalised, semantic type. Everything below belongs to this node until the next @Type.
@Type|Type Name
Multi-type node. Both types apply simultaneously. First type is primary. Queries for either type match.
→ file.kgl
Hard link. Strong relationship. Signals: follow this to fully understand the current node.
→ file.kgl#Name
Fragment link. Hard link to a specific named node inside a multi-node file.
~> file.kgl
Soft link. Weak relationship. Background context — fetch only when depth is needed.
[rel] → file.kgl
Named relationship. Hard link with a typed edge. Indented lines below are relationship properties.
[rel|rel] → file.kgl
Multi-type relationship. Both labels apply simultaneously. Use when a relationship genuinely carries two meanings.
key: value
Relationship property. Indented under a link. Scoped to that relationship only.
key: value
Node field. Any key, freeform value. No required fields unless a schema defines them.
#tag
Tag. For clustering and discovery. Multiple per node.
---
Body separator. Everything after is free prose — not parsed as fields. For context and nuance fields can't capture.

Multiple nodes in one file

A single .kgl file can contain multiple nodes. Each @Type declaration starts a new node. Group tightly related nodes (a person and their skills); keep loosely related nodes in separate files.

people/alice.kgl
# people/alice.kgl

@Person Alice Nguyen
joined: 2021-03-15
#backend #python

[mentors] → people/diana.kgl#Diana Park
    started: 2023-06
    focus: "backend systems and code review"

→ projects/search-revamp.kgl
~> teams/platform.kgl


@Person|Contractor Ravi Mehta
joined: 2024-06-01
contract-end: 2025-03-31

[reports-to] → people/alice.kgl#Alice Nguyen
    note: "day-to-day lead, not official manager"

→ projects/search-revamp.kgl

Optional. Powerful.

Schema files define node types, required fields, and allowed relationships. Violation is a warning, not an error — files without required fields are valid KGL. Add schema validation when your graph matures.

Schema lookup for any .kgl file: local _schema.kgl first, then walk up to root. First definition of a type wins. Local schemas extend the root; they don't replace it.

Node type definition

_schema.kgl
@NodeType Person
    joined: date (required)
    role:   text
    github: url

@NodeType Contractor
    extends: Person
    contract-end: date (required)
MarkerMeaning
field: type (required)Required — linter warns if absent
field: typeOptional — documented but not enforced
extends: TypeInherit all fields from named type

Relationship type definition

_schema.kgl
@RelType staffed-by
    from: Project
    to:   Person
    role:  text (required)
    since: date (required)

@RelType depends-on
    from: Project Service
    to:   Project Service
    since:       date
    sunset-date: date
    note:        text

The from:/to: fields constrain which node types may appear on each side — list more than one, space-separated, to allow any of them. Relationship properties are just more field lines below from:/to:, same (required) syntax as node fields.

Linter output

linter warnings — people/charlie.kgl
@Person Charlie Watts
# ⚠  missing required field 'joined' (root _schema.kgl · @NodeType Person)
# ⚠  missing required field 'team'   (people/_schema.kgl · @NodeType Person)

[depends-on] → people/diana.kgl#Diana Park
# ⚠  type mismatch: 'depends-on' expects Project|Service → Project|Service
#    source is @Person — relationship not allowed here

[staffed-by] → projects/search-revamp.kgl
# ⚠  missing required property 'role'  on relationship 'staffed-by'
# ⚠  missing required property 'since' on relationship 'staffed-by'

Everything in one place.

What's built.

KGL 0.2 has a tested Python implementation covering the full spec, both reading and writing. One thing is still ahead: enum constraints in schema.