Modes and storage
A mage knowledge base is just files in a git repo. The question this page answers is which git repo those files live in — and how mage finds them. There are two basic shapes, and one way to connect them.
The two shapes
Section titled “The two shapes”An in-repo knowledge base
Section titled “An in-repo knowledge base”The simplest setup: the knowledge base lives inside the code repo it describes, in a mage/ directory at the repo root.
my-service/ src/ mage/ metadata.json # mode: in-repo INDEX.md notes/ decisions/ work/You create it with:
mage init --in-repoflowchart TD i["mage init --in-repo"] --> s["creates the repo's mage/<br/>notes · decisions · work · metadata.json · INDEX.md"] s --> c["mage connect — wires capture + recall"] c --> w["work → the loop → groom → notes/ → you commit"]
The notes travel with the code, in the same history, behind the same branch protection. This is the right default when one repo’s knowledge belongs to that one repo.
A standalone hub
Section titled “A standalone hub”A hub is a knowledge base that is its own repo, not nested in any one code repo. It is two things at once:
- a knowledge base in its own right — with its own top-level
notes/,decisions/,work/, andINDEX.mdfor cross-cutting knowledge that spans the whole system, and - a registry of projects under
projects/<name>/, each a wing for one code repo.
my-hub/ metadata.json # the registry of projects INDEX.md # the hub's OWN index notes/ decisions/ work/ # the hub's own cross-cutting knowledge projects/ engine/ # one project's flat docs root — its notes live here web/ # anotherNotice the layout is flat: a project’s notes live at projects/<name>/notes/, not projects/<name>/mage/notes/. There is no second mage/ nested inside the hub, because the hub root already is a mage knowledge base. A project looks like the hub it lives in, not like a code-repo mage/ (this was settled in ADR-0011 and ratified in ADR-0023).
The hub having its own notes/ and projects/<name>/ is intentional scope separation, not duplication: the hub’s own notes hold what spans the whole fleet (the shared architecture, the conventions every project obeys); each project’s notes hold what is scoped to that one code repo.
You create a hub with:
mage init --hub <your-hub>mage init is detection-first: run it bare inside a git repo and it scaffolds an in-repo knowledge base; run it bare somewhere that is not a git repo and it creates a standalone hub. The --in-repo and --hub flags make the choice explicit (useful for agents and CI). A bare hub name becomes ./<name>; a path is used as-is, like git init.
mage never runs git for you.
mage initprints the exact commit command and stops — an agent will never land a surprise commit.
Linking a code repo to a hub
Section titled “Linking a code repo to a hub”Once a hub exists, you connect a code repo to it with mage link. This is where storage kind comes in — it decides who owns the project’s notes.
mage linkRun from inside the code repo, mage link registers it with a hub and auto-detects the storage kind from whether the repo already has mage/ content. There are two kinds:
- repo-owned — the notes stay in the code repo’s own
mage/directory; the hub just registers awareness of the project and links to it. The code repo is the source of truth. In mage’s metadata this code repo’s mode ishybrid: an in-repo knowledge base that is also known to a hub. - hub-owned — the notes live in the hub at
projects/<name>/; the code repo carries no notes of its own, only a pointer to the hub. The code repo’s mode here isexternal.
You can override the auto-detection:
mage link --storage repo-owned # hybrid: the repo keeps its docsmage link --storage hub-owned # external: the hub owns the docsTo undo a link, mage unlink removes the linkage from both sides’ metadata.
flowchart TD
h["mage init --hub"] --> hs["the hub<br/>notes · decisions · projects/ · metadata.json"]
hs --> l["in a code repo: mage link"]
l --> q{"who owns this repo's notes?"}
q -->|hub-owned| e["live in the hub · projects/name/<br/>repo mode = external"]
q -->|repo-owned| r["live in the repo · mage/<br/>repo mode = hybrid"]The shapes, side by side
Section titled “The shapes, side by side”| Shape | Where notes live | metadata mode | Storage kind |
|---|---|---|---|
| In-repo | <code-repo>/mage/ | in-repo | (none — no hub) |
| Hybrid | <code-repo>/mage/ | hybrid | repo-owned |
| External | <hub>/projects/<name>/ | external | hub-owned |
| Hub (itself) | <hub>/notes/ etc. | (the hub’s own registry) | (it owns its own notes) |
All four are knowledge bases. The distinction is purely about which repo holds the files.
How mage finds the knowledge base
Section titled “How mage finds the knowledge base”Most mage commands need to locate the docs root to operate on. They do this by walking up the directory tree from where you run them (this is resolveDocsRoot in src/paths.ts):
- Look upward for a code repo with
mage/metadata.json.- If its mode is
in-repoorhybrid, the docs root is that repo’smage/. - If its mode is
external, mage follows thehub_pathin the metadata to the hub’sprojects/<name>/— so captures and grooming land in the hub, where the notes actually are, not in the code repo.
- If its mode is
- Otherwise, look upward for a hub root (a directory with a
projects/registry and a top-levelmetadata.json). Inside aprojects/<name>/directory it resolves to that project’s flat docs root; anywhere else under the hub it resolves to the hub root itself.
This is why you can run mage commands from anywhere inside a repo or hub and they find the right knowledge base. It is also why an external-mode code repo’s captures end up in the hub even though you were working in the code repo — the metadata pointer redirects them.
The on-disk schema is stamped as mage.v2. Older mage.v1 metadata is read leniently and upgraded in memory; mage migrate rewrites it to the current schema (and, like init, never commits).
Where to next
Section titled “Where to next”- Install and Quickstart — run
mage initand capture your first note. - Notes and The graph: wings and rooms — what lives inside whichever shape you choose.
- Reference: commands — every flag on
init,link,unlink, and the rest.