docujoint

Documentation that parses

Declare the format once.
Everything else is derived.

docujoint is a deterministic engine for markdown documentation. You describe your format in one YAML file — types, typed tables, evidence schemes, the rules that matter to you — and it parses every document into data it can check, chart, answer questions about, and refuse to let drift.

$npm i -g docujoint MIT · Node 20+ · then dj init
What you write · Checkout Page.md
## Features
| ID | Feature            | Implemented           | Gap |
|----|--------------------|-----------------------|-----|
| f1 | Buyer pays by card | repo://shop/pay.ts    | —   |
| f2 | Saved wallets      | repo://shop/wallet.ts | —   |
| f3 | Apple Pay          | —                     | not built yet |
What docujoint derives
f1 Buyer pays by card Cites code the scan confirms. Nothing to do. Built
f2 Saved wallets wallet.ts is gone from the inventory — the claim outlived the code. Drift
f3 Apple Pay A gap with no evidence. Counted, not hidden. Missing

01 · The format is yours

Your rules, not ours.

The engine ships no vocabulary. It does not know what built means, or that a table called Features is special. Your format.yaml says which document types exist, what tables they carry, which columns are evidence, and how state is computed from them.

Two teams can use the same word for opposite things, and both are right.

format.yaml
blocks:
  features:
    heading: Features
    columns:
      - { name: ID, id_grammar: "f<n>", unique: error }
      - { name: Implemented, refs: [repo, api] }
      - { name: Gap }
    # state is computed, never typed into a column
    derive:
      state:
        - { when: "implemented && !gap && ref_broken", then: drift }
        - { when: "implemented && !gap", then: built }
        - { when: "!implemented && gap",  then: missing }
    forbid_columns:
      - { name: Status, severity: error }

02 · Nothing is stored twice

A status column is a lie waiting to happen.

Anything that can be computed is computed: from what the row says, and from whether the code it cites still exists. A document cannot claim done while pointing at a file nobody has — that combination has a name, and it shows up as a number.

Point it at a scan of your codebase and the reverse also holds: what exists and nothing documents.

dj lint --all
  Apps/Storefront/Pages/Checkout Page.md
    error  f2: cites evidence the scan no longer confirms   (drift)
    warn   f5: complete but no test:// evidence            (feature-untested)
    warn   q1: open question blocks f3                    (unspecified)

  498 file(s), 488 concept(s): 0 error(s), 627 warning(s)
  exit 0 — pass --warnings-as-errors to make CI mean it

03 · One vault, three surfaces

Read it, work it, ask it.

The same parse becomes an interactive dashboard you declare in YAML — a page per team member, tables you act on in place, forms that write answers back through the linter so a bad edit is refused rather than committed.

And a graph: every document, row, link and citation projected into SQLite, so "what would this change touch" is a query instead of an afternoon.

dj graph --out kb.db
  4,187 nodes (488 document · 3,699 artifact)
  7,408 edges (4,394 rows · 1,960 citations · 631 links · 423 joins)

  -- who cites the endpoint you are about to change?
  SELECT r.node, r.key FROM cite c
    JOIN row_ r ON r.id = c.row
   WHERE c.uri = 'api://payments/charges';
  11 rows · 0.03 ms

Measured on a 498-file vault

0.20sto parse 498 documents into checkable data
0.12sto project the whole graph into SQLite
0.03msto answer who cites a given endpoint
0numbers on a dashboard that anyone maintains by hand

Sixty seconds

From nothing to a dashboard.

terminal
# a working vault: format, dashboard, sample documents
$ npm i -g docujoint
$ dj init my-docs && cd my-docs

# zero findings is the bar, and CI can hold you to it
$ dj lint --all --vault vault --format format.yaml

# one self-contained file — opens offline, no server
$ dj dashboard --vault vault --format format.yaml \
       --dashboard dashboard.yaml --out dashboard.html

# or serve it, and answer questions in the page
$ dj serve --vault vault --format format.yaml \
       --dashboard dashboard.yaml
For coding agents

The referee, not the author

Agents write documentation and the engine decides when it is done — zero errors, deterministically, from the same binary CI runs. dj skills install --all teaches them the format.

Built on OKF

A profile, not a silo

Your format is a profile over Google's Open Knowledge Format. One markdown file per concept, ordinary links, no database — a vault stays readable in any editor and diffable in any repo.

Search that understands

Ask in any language

Full-text over every row, and semantic search when you bring an embedding model. A Spanish question finds the right English rows — the engine stores the vectors and never calls a model itself.

Open core

The tools are free. The infrastructure is the product.

MIT · free forever

docujoint, the engine

  • Parse, lint, and enforce your format in CI
  • Interactive dashboards, declared in YAML
  • Forms that write back through the linter
  • The graph projection and full-text search
  • Authoring skills for coding agents
Hosted · in progress

docujoint Cloud

  • Knowledge bases hosted per team, with access control
  • Dashboards your whole company can read, always current
  • A durable feedback ledger, separate from the documents
  • An agent that explores a corpus and proposes views