Getting Started¶
This guide takes you from an empty directory to a linted, indexed wiki in a few minutes.
Create a workspace¶
A Keep workspace is any directory containing a keep.yml file. Create
one:
mkdir my-wiki
cd my-wiki
Add keep.yml¶
Every workspace needs a keep.yml at its root. The minimal configuration
is:
schema_version: 1
That is all you need to start. See Configuration for the full list of options.
Write your first document¶
Create recipes/soffritto.md:
---
slug: soffritto
title: Soffritto Base
kind: recipe
status: canon
date_created: "2025-11-03"
tags: [italian, base, vegetables]
summary: Foundational aromatic base of onion, celery, and carrot for Italian sauces.
---
Soffritto is the backbone of Italian cooking.
The frontmatter fields are explained in the frontmatter reference.
Run keep state¶
State tracking is the foundation for staleness detection. Run it first:
keep state
# state written to .keep/state.json (1 document(s))
Keep hashes each document’s meaningful fields and records the last modification timestamp. Run this after any editing session.
Run keep graph¶
keep graph
# graph written to .keep/graph.json (1 node(s), 0 edge(s))
Add a second document with a relation¶
Create recipes/ragu.md:
---
slug: ragu
title: Ragù alla Bolognese
kind: recipe
status: published
date_created: "2026-01-10"
tags: [italian, pasta, meat]
summary: Classic slow-cooked Bolognese built on a soffritto base.
relations:
- target: soffritto
type: derived_from
---
A proper Bolognese takes time. The soffritto is the foundation.
Rebuild the graph:
keep graph
# graph written to .keep/graph.json (2 node(s), 1 edge(s))
Run keep index¶
keep index
# index written to index.md (2 document(s))
Open index.md to see the generated catalog — the file an LLM reads at the
start of every session to orient itself.
Run keep lint¶
keep lint
# lint: ✓ clean — 2 document(s), 0 hard violation(s), 0 warning(s), 0 injected
The linter exits with code 1 when hard violations are found, making it
suitable as a CI gate. See lint violations for a full list
of what is checked.
Reciprocal auto-injection¶
Change ragu’s relation to supersedes:
relations:
- target: soffritto
type: supersedes
Run keep lint:
keep lint
# lint: ✓ clean — 2 document(s), 0 hard violation(s), 0 warning(s), 1 injected
#
# soffritto [canon]
# ↩ injected 'superseded_by' from 'ragu'
Keep automatically wrote a reciprocal superseded_by relation into
soffritto.md, marked auto_injected: true.
Frontmatter reference¶
Every document must have a frontmatter block between --- delimiters.
Field |
Type |
Required |
Description |
|---|---|---|---|
|
string |
yes |
Unique identifier. URL-safe, kebab-case. |
|
string |
yes |
Human-readable document title. |
|
string |
yes |
Semantic document type. Examples: |
|
string |
yes |
One of: |
|
ISO 8601 |
yes |
Creation date. Format: |
|
list |
yes |
Freeform tags for discovery. |
|
string |
no |
One sentence describing the document. Populates |
|
boolean |
no |
Excludes from |
|
list |
no |
Typed directed edges to other documents. |
Lint violations¶
Hard violations (block promotion to published or canon):
dangling_slug— a relation target does not existprivate_target_in_public_doc— a public document relates to a private onemissing_reciprocal— a symmetric relation has no backlinkinvalid_promotion— a gated document has outstanding violations
Soft warnings (advisory):
incomplete— missing recommended fields or insufficient relationsstale— no meaningful modification within the status threshold