Skip to main content

vault2okf

CI PyPI Python License: MIT

Convert an Obsidian vault into an Open Knowledge Format (OKF) bundle, then check and repair any OKF bundle before an agent or a teammate reads it.

vault2okf converting a vault, then catching a broken link and a note without frontmatter

OKF is a folder of markdown files with YAML frontmatter, where each file is one concept and concepts link to each other with normal markdown links. An Obsidian vault is close to that, but its [[wikilinks]] only work inside Obsidian and its notes carry no type. vault2okf closes the gap and then keeps the bundle healthy.

  • vault2okf convert turns a vault into a conformant bundle: wikilinks become real links, embedded files are copied, frontmatter and a per-folder index.md are written. The vault is never modified, and re-runs change only what changed.
  • vault2okf validate checks any bundle against the OKF v0.2 spec: frontmatter, reserved files, trust and lifecycle fields, broken links and heading anchors, leftover wikilinks, orphan notes, and file names that break on Windows or macOS. It prints plain text, JSON or GitHub annotations.
  • vault2okf fix repairs the common problems in place: missing type, title and description, tags written as one string, wikilinks, an unquoted okf_version.
  • vault2okf init, schema and rules scaffold a bundle, print a JSON Schema for concept frontmatter, and list every rule.

It also works as a Python library, a GitHub Action and a pre-commit hook. Settings can live in the repository, in vault2okf.toml or pyproject.toml.

Install

pipx install vault2okf      # or: uv tool install vault2okf

Needs Python 3.10 or newer.

Quick start

vault2okf convert ~/Documents/MyVault ./knowledge
vault2okf validate ./knowledge
$ vault2okf validate knowledge

Home.md
    L13  warning OKF030 link target not found: /Runbooks/deploy.md

todo.md
         warning OKF031 no document in the bundle links to this concept
     L1  error   OKF001 file must start with a `---` YAML block

not conformant: 4 concepts, 10 links, 1 error, 2 warnings

Exit codes: 0 conformant, 1 validation failed, 2 usage error.

Why vault2okf

okf-cli also converts markdown to OKF. It is built for plain markdown folders and adds commands to browse a bundle. vault2okf is built for Obsidian vaults and for catching and fixing problems. Both tools were run in September 2026 on the vault in examples/vault and on a test bundle with eight planted problems:

vault2okf 0.1.0 okf-cli 0.6.1
[[wikilinks]], aliases, heading links, ![[embeds]] converted to markdown links left unchanged
Embedded images and files copied into the bundle not copied
Frontmatter, type and index.md checks yes yes
Timestamp, status and generated checks yes no
Broken links, leftover wikilinks, orphans, log.md dates reported not reported
Planted problems reported (out of 8) 8 1
Repairs problems in place yes (fix) no
JSON and GitHub annotation output yes no
list and read commands, generated AGENTS.md no yes

The two install different commands (vault2okf and okf), so they can sit side by side.

The validator finds no errors in the four sample bundles in Google's OKF repository. One of them, stackoverflow, gets eight warnings because its tags are written as one string instead of a list; vault2okf fix rewrites exactly those eight lines.

What convert does

In the vault In the bundle
[[Note]], [[folder/Note]] [Note](/folder/Note.md), resolved the way Obsidian does (full path, then unique name, then closest folder)
[[Note|alias]], [[Note#Heading]], [[#Heading]] [alias](/Note.md), [Note > Heading](/Note.md#heading), [Heading](#heading)
[[ship]] where no note is named ship but one lists it in aliases: a link to that note, listed in the report (Obsidian itself leaves such links unresolved)
![[image.png]] ![image](/image.png), with the file copied into the bundle
![[Other note]] a normal link to the note
[[missing]] plain text, listed in the report
%% comments %% removed, because Obsidian never shows them and they often hold private notes; --keep-comments keeps them
tags: and inline #tags one tags list; anything inside code, links, HTML or comments is ignored
no type Note, or set one with --type-map or --type-from-folder
first # Heading title, falling back to the file name
first paragraph description, one sentence of at most 200 characters; notes without a paragraph use their first callout, list item or section headings
file modified time generated: { by: vault2okf/x.y.z, at: ... }
other frontmatter (aliases, cssclasses, ...) kept as is
notes named index.md or log.md renamed index-note.md / log-note.md, since OKF reserves those names
names Windows rejects (What? Is: this.md, CON.md) or that differ only by case renamed as little as possible (What Is- this.md, CON_.md, case (2).md) with every link updated
.obsidian/, hidden files, symbolic links skipped

It also writes an index.md in every folder (notes grouped by type, with okf_version: "0.2" in the root one) and a root log.md. Every rename, alias link and unresolved link is listed in the report.

vault2okf convert vault out \
  --type-map 'Daily/*=Daily Note' \
  --type-map 'Runbooks/*=Runbook' \
  --exclude 'Private/*' \
  --attachments referenced      # referenced (default) | all | none

Updating a bundle

Run the same command again with --force after the vault changes:

$ vault2okf convert vault knowledge --force
updated knowledge: 1 concept added, 2 changed, 0 removed, 41 unchanged

Only files whose content changed are written, so unchanged files keep their timestamps and a git diff shows just the real changes. generated.at moves only when a note's content changes, not when a fresh checkout resets file times. log.md keeps its history and gains an entry per change, newest first:

## 2026-09-26
* **Creation**: Added [Kubernetes](Hosting/Kubernetes.md).
* **Update**: Changed [Vercel](Hosting/Vercel.md).
* **Removal**: Removed `Hosting/Heroku.md`, which is no longer in the vault.

Safety: convert never follows symbolic links, so no file from outside the vault can end up in the bundle. It refuses to write inside the vault or into a non-empty folder it did not create, and it only ever removes files listed in its own .vault2okf.json manifest, after the new files are written. --dry-run reports without writing anything.

What validate checks

vault2okf rules prints the full table. Severity follows the spec: things a bundle must do are errors, things consumers must tolerate are warnings.

Code Severity Meaning
OKF001 error Concept has no frontmatter block
OKF002 error Frontmatter is not valid YAML
OKF003 error type missing, empty or not a string
OKF004 error Frontmatter is not a mapping
OKF010 error index.md carries frontmatter other than a root okf_version
OKF020 error log.md date heading is not YYYY-MM-DD
OKF040 error Malformed generated, verified, sources, status, stale_after or attested-computation fields, or a timestamp without a UTC offset
OKF030 warning Broken link (the spec says consumers tolerate this)
OKF031 warning Orphan concept, nothing links to it
OKF033 warning Leftover [[wikilink]]
OKF034 warning File or folder name Windows rejects, or two names that differ only by case
OKF035 warning Link to a #heading that does not exist in its target (GitHub's anchor rules)
OKF041 / OKF042 warning Missing recommended title / description
OKF043 warning generated.by or verified[].by is not <producer>/<version>, human:<id> or process:<id>
OKF044 warning Recommended field of the wrong type, such as tags written as one string
OKF050 warning stale_after has passed

A concept containing only type is fully conformant, and unknown types and keys are never errors.

vault2okf validate bundle --strict               # warnings fail the run too
vault2okf validate bundle -i OKF031 -i OKF042    # skip rules
vault2okf validate bundle --format json          # machine readable
vault2okf validate bundle --format github        # inline annotations in Actions
vault2okf validate bundle -x 'drafts/*'          # skip paths

Fix a bundle

vault2okf fix works on any OKF bundle, whoever produced it, and changes files in place. Commit first, or preview with --dry-run.

$ vault2okf fix knowledge
Home.md: set type to 'Note', added title, added description, converted 1 wikilink
Hosting/Vercel.md: added description, wrote tags as a list, converted 1 wikilink
fixed 7 problems in 2 files
left for you:
  Hosting/Vercel.md: [[Docker]] matches no file in the bundle

validation

Hosting/Vercel.md
     L7  warning OKF033 [[Docker]] is a wikilink; use [text](/path.md)

conformant: 2 concepts, 2 links, 0 errors, 1 warning

It adds a missing type (--default-type, Note unless set), title (first heading, else the file name) and description (first sentence), writes string tags as a list, turns numbers into text where text belongs, converts wikilinks that match a file in the bundle, and quotes an unquoted okf_version. Only the lines that change are rewritten, so YAML comments, other keys and the file's line endings are kept. Anything that needs a person, like invalid YAML or a link to a note that does not exist, is listed instead of guessed.

Settings file

Put defaults in vault2okf.toml, or under [tool.vault2okf] in pyproject.toml. The nearest one at or above the current folder is used, and flags on the command line always win. --config picks a file by path.

[convert]
type-from-folder = true
type-map = { "Daily/*" = "Daily Note", "Runbooks/*" = "Runbook" }
exclude = ["Private/*"]
attachments = "referenced"      # referenced | all | none
keep-comments = false

[validate]
strict = true
ignore = ["OKF031"]
exclude = ["drafts/*"]

[fix]
default-type = "Note"

In pyproject.toml the sections become [tool.vault2okf.convert], [tool.vault2okf.validate] and [tool.vault2okf.fix]. Unknown sections, settings and rule codes are errors, so a typo never passes silently.

GitHub Action

- uses: actions/checkout@v4
- uses: abhishekacharya200/vault2okf@v0.1.0
  with:
    path: knowledge
    strict: "true"

pre-commit

repos:
  - repo: https://github.com/abhishekacharya200/vault2okf
    rev: v0.1.0
    hooks:
      - id: okf-validate
        args: [knowledge]

Library

from vault2okf import ConvertOptions, convert_vault, fix_bundle, validate_bundle

convert_vault("MyVault", "knowledge", ConvertOptions(type_from_folder=True))
fix_bundle("knowledge")

report = validate_bundle("knowledge", ignore=["OKF031"])
for d in report.diagnostics:
    print(d.code, d.path, d.line, d.message)

vault2okf schema > okf-frontmatter.schema.json gives editors a JSON Schema for concept frontmatter.

Known limits

  • Indented (4-space) code blocks are not detected by the converter, so a [[link]] inside one is still converted.
  • Block references ([[Note#^id]]) lose the block part and link to the note.
  • Dataview, Templater and other plugin syntax is copied as is.
  • fix cannot repair frontmatter that is not valid YAML; it lists those files instead.

Development

uv sync
uv run pytest --cov=vault2okf
uv run ruff check . && uv run ruff format --check . && uv run mypy src

See CONTRIBUTING.md. This project is not affiliated with Google. OKF is an open specification published by the Google Cloud Data Cloud team.

License

MIT

Release files for vault2okf 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vault2okf 0.1.0
File Size Uploaded
vault2okf-0.1.0.tar.gz 50.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vault2okf 0.1.0
File Interpreter ABI Platform
vault2okf-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 94.0 kB

Release files / vault2okf-0.1.0.tar.gz

Download URL vault2okf-0.1.0.tar.gz
Size 50.6 kB
Tags Source
SHA-256 checksum
How to use checksums
0e2f801ab16c807a0d92bed13671cac163e7af366e4c9ee0cc8be4bb336c352d
BLAKE2b-256 checksum
How to use checksums
69a5d28ef1807d3d95b39f66be56c1a4e99e48b87136ca06d9d7bb7f49c2d7e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / vault2okf-0.1.0-py3-none-any.whl

Download URL vault2okf-0.1.0-py3-none-any.whl
Size 43.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c40954e41ae32a8e686e497ec70a531ddccb26386e11000cbfe4b2ee19c563de
BLAKE2b-256 checksum
How to use checksums
005cdc79e06c79df590933e89e132785f58e19e998315a1adcbbcc56afeeae3c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page