GEML is an Agent-Native base document format and protocol, for people and AI agents to read and write the same document.
One format, two readers. For people, plain text that reads clean; for agents, a "Doc-as-a-Base" — addressable, verifiable, traceable, revertible.
GEML is minimal.
It is plain text — still clean with no renderer in sight;
one block syntax for the whole language;
addressable, verifiable, referenceable structure, natively.
The Doc-as-a-Base Manifesto
Documents no longer need just a format.
They need a set of verbs.
Just as REST gave scattered resources one naming scheme (the URI) and one shared set of verbs, Doc-as-a-Base gives every block of a document one naming scheme (#id) and one shared set of verbs (get / set / add / delete).
A document that is still plain text, but comes with its own verbs: every block has a name and can be fetched alone; references are verified, and a broken one turns the build red; an embed is a lookup, not a copy; a revert rolls back one block instead of redoing the whole page. It is the base of every deliverable — .md and .html are views projected from it.
In the new paradigm, we value
- Addressing by blockover reading and writing whole documents
- References that fetchover copy and paste
- Errors at build timeover silent rot
- Rolling back one blockover redoing the whole page
Why now
Because the reader has changed
For decades a text was optimized either for human reading (Markdown, Word) or for machine parsing (JSON, Schema). In the LLM era, humans and agents co-read, co-author, and rewrite the same document for the first time.
The old ways break down: every time we provide context or prompts, we manufacture copies. The engineering source of truth gets duplicated, fragmented, and eventually drifts away. A copy is drift from the moment it is made.
The argument at essay length: "Why Do We Need a New Text Format in the Era of LLMs?" · 中文版
To solve this, the format carrying the text has to provide four capabilities at the syntax level:
Block-level addressing
Every block has a unique #id. We no longer address just the whole file.
Reference-based projection
Assemble context by looking values up, not copying them — === embed.
Build-time validation
A broken reference is a build error, not a silent 404 found later.
Block-level rollback
Roll back one block, independent of Git, via a .gemlhistory sidecar.
The format in 1 minute
One shape, every kind of content
A block is === type [attributes] … ===. Only the type — and how its body is read — changes.
Typed blocks
Code is a block. So are tables, diagrams, math, callouts, even metadata — and a run of prose can be one too (=== text), whenever you want it addressable. The shape is the same every time, which makes the language easy enough to learn that it's hard to get wrong.
The type decides how the body is read: raw (verbatim — code, diagram, math, table), flow (parsed prose — note, text), or data (one key=val per line — meta).
=== code {#hello lang=python}
print("hi")
===
=== note {.intro}
Prose with *emphasis* and a
[[#budget]] reference.
===
=== meta
title = "Budget plan"
====== table {#fy25 format=csv header=1}
Segment, Q1, Q2, Q3, Q4
Cloud, 8, 10, 12, 14
Platform, 5, 6, 7, 9
===
=== view {#fy25-report src=#fy25
compute="FY [%.1f] = Q1+Q2+Q3+Q4; n = 1"
summary="Segment = 'Total';
FY [%.1f] = sum(FY); n = sum(n)"}
===A view derives
Write a table visually with pipes or as CSV data — both describe the same model, and both hold facts. A view over one derives: compute runs arithmetic per row, summary adds a foot row from sum / avg / min / max / count, and where / order / limit / select / by filter, sort and aggregate.
A chart binds straight to either — data=#fy25-report — so there is a single source of truth and the column references are checked at build time.
Data — a value, not just text
Every type names what it holds. data holds a data value — json (the default), jsonl, and yaml for a declared subset; toml reserved. Being typed means the body is read, not just displayed: a missing comma fails the build, geml get --json returns the value itself, and a chart can bind straight to it.
Records can also stay in their own file — src=ops/latency.jsonl#L900-999 names the file and, optionally, a line window — so a log keeps being appended and tailed as before, while the document is its verified, addressable, chartable view of it.
=== data {#log format=jsonl}
{"ts":"09:00","p95":41}
{"ts":"09:10","p95":58}
===
=== diagram {format=geml-chart
data=#log type=line x=ts y=p95}
===Embeds — a dynamic reference, not a copy
One block can stand for another, in the same document by src=#id or across documents by src=other.geml#id. An embed is a dynamic lookup at render time: change the source once and every embed follows. The body stays empty; the target lives in src=.
If the target goes missing, geml check fails the build — a reference is a lookup, not a signpost. See it live: with the browser extension installed, the Transclusion section of sample.geml renders same-document, cross-document, and chained projections in place.
=== embed {src=#fy25}
===
=== embed {src=spec.geml#grammar}
===What's different
Four capabilities, one plain-text format
Each capability has mature solutions in its own field. What's unusual is meeting all four at once, in plain text.
| Family | Addressable | Projectable | Verifiable | History |
|---|---|---|---|---|
| Word / Google Docs | ❌ | ❌ | ❌ | ⚠️ platform-side |
| Markdown / AsciiDoc | ⚠️ anchors only | ⚠️ breaks silently | ❌ | ❌ external git |
| JSON / XML | ✔️ | ⚠️ XML only | ✔️ external tooling | ❌ external git |
| GEML | ✔️ #id per block | ✔️ embed | ✔️ build error | ✔️ .gemlhistory |
Markdown owns the mainstream surfaces, so GEML positions itself as the editing source of truth, not the delivered artifact — project one way with geml <file> --to md|html and ship .md or .html as before. Collaboration, not lock-in. (Projection is lossy: block ids and table-bound charts don't survive it.)
Design boundaries — GEML stays small on purpose
- No raw-HTML escape hatch — semantics stay portable, tied to no backend or renderer.
- Hosts external diagram DSLs (Mermaid, Graphviz, D2, …) rather than inventing one.
- Tables compute, but aren't a spreadsheet engine — per-row formulas and aggregates, not cell addressing or macros.
- ATX headings only — no setext, no
---frontmatter, no thematic-break guesswork.
With an LLM
Written and edited by models — precisely
To change one thing, an agent needn't re-read and re-emit the whole document: it addresses a single block by id, then validates.
npm i -g @geml/geml # the `geml` command (Node 22+)
geml list doc.geml # CALL FIRST: every block, address, kind
geml find "words" doc.geml # search content -> an address, not a line
geml get doc.geml '#hello' # print ONE block by name
geml set doc.geml '#license' --in - # replace a block from stdin
geml add doc.geml --after '#intro' --in snippet.geml
geml rename doc.geml '#old' '#new' # rename an id + every reference to it
geml revert doc.geml '#plan' --rev -1 # roll ONE block back
geml check doc.geml # validate: diagnostics + exit codeEvery mutation writes the whole updated document — in place for a file, to stdout for - — and each is re-parsed before the write and refused if it would break the document.
An MCP server ships with the package
Your agent edits one block at a time instead of rewriting files. A write is parsed before it reaches disk and refused with diagnostics if it would break the document; every write first records a .gemlhistory revision, so a bad edit is both prevented and undoable.
One command, or a plugin for your harness
npx -y @geml/geml skill install installs the authoring skill, the CLI, and the user-scope MCP registration. Packaged plugins exist for Claude Code, Codex, and DeepSeek Harness. For any other model, paste the primer from the README and run geml check on the output for a hard pass/fail.
A gift for programmers
Your whole codebase's call graph, written as GEML
A demanding test of GEML's expressive power, and above all of block-level bidirectional linking: geml codemap build lays a call graph out as a tree of GEML documents — every method an #id block, with #calls / #called-by edges both ways.
The downstream chain (what a method calls) for troubleshooting, the upstream chain (who calls it) for blast radius — visible in a second, queryable from the shell or by an agent.
geml codemap build # detect languages -> index -> one merged graph
geml codemap serve # opens your browser on the graphScale is measured, not promised: on Apache Flink — 13,585 Java files, ~81,000 methods, 266,821 call edges — the plain-text data tables still open and query instantly. TS/JS needs zero setup; other languages take one Joern download.
Read more in the README →Ecosystem & maturity
Small and young — but stable
Spec 1.0 is released and usable for real documents, with a strict conformance suite, a reference implementation that passes it, and an open proposal process.
- Self-hosting — the specification itself is written in GEML and parsed clean on every test run.
- A conformance suite a second parser — written from the spec alone, importing nothing from the reference implementation — must reproduce case for case. Two implementations agreeing is what keeps subtle rules from drifting; both are still by the same author, and one by someone else is what this project most wants.
- 1,300+ checks in
npm test, coverage CI-gated at ≥95% lines / statements / functions / branches. - "Stable" means the rules already in 1.0 won't shift under you; a breaking change bumps the spec version and ships with updated conformance cases.
Two honest caveats. No mainstream surface renders .geml natively yet — the browser viewer, the CI Action, and one-way projections are how it travels today. And models are less fluent in it than in Markdown, because nothing was pre-trained on GEML at scale; the uniform block syntax and --json diagnostics let an agent check and repair its own output, but the starting fluency really is lower.
Get hands-on
Three ways in
1. Try it live
Edit on the left, rendered on the right, and the build verdict flips red the moment a reference breaks. No install, nothing to read first.
Open the Playground →2. Run it locally
npm i -g @geml/geml
geml check doc.gemlOr point it at your own repo with geml codemap build.
3. Read the grammar
The full spec is normative and short enough to read in a sitting.
Specification →