CASOON Open Source
Keep a Markdown knowledge base honest.
knowledge-lint checks a folder of Markdown entries with YAML frontmatter against the categories, types and status values you declare in one _types.yml. Undeclared folders, broken links, unencrypted secrets and missing attachments fail the run instead of drifting silently.
cargo install knowledge-lintcargo install --git https://github.com/casoon/knowledge-lintChecking broken-knowledge-base/secrets/api-token.yaml ERROR: file in a sops_secret category has no `sops:` metadata field — not SOPS-encrypted? Checking broken-knowledge-base/decisions/2024-03-hosting.md WARNING: last_reviewed is 927 days old (> 365), but the entry is marked 'stable' ERROR: related link '../glossary/cdn.md' points to a file that doesn't exist Checking broken-knowledge-base/decisions/2026-02-backups.md ERROR: status 'current' is not one of the allowed values (draft, stable, deprecated) ERROR: type 'process' does not match category 'decisions' (expected: 'decision') Checking broken-knowledge-base/glossary/embedding.md WARNING: possible API key with prefix 'sk-' found — check whether this is a real secret ERROR: source is missing ERROR: last_reviewed '12.01.2026' is not a valid date (YYYY-MM-DD) Checking broken-knowledge-base/meetings/ ERROR: category 'meetings' is not declared in _types.yml Checking broken-knowledge-base/_attachments.yml WARNING: attachment 'contract-scan' has no description ERROR: attachment 'contract-scan': path './assets/contract-2026.pdf' does not exist ERROR: attachment 'contract-scan': referenced_by entry 'decisions/2026-03-hosting-contract.md' points to a file that doesn't exist 0 of 3 knowledge entries are marked public. 9 error(s), 3 warning(s) found.
- category kinds: entries, SOPS secrets, assets
- 3
- commands: lint and clean
- 2
- plaintext key markers scanned in every entry
- 10
- exit code for warnings only / any error
- 0 / 1
What it does
Categories are declared, not guessed
Every folder in the knowledge directory needs an entry in _types.yml with its kind and expected type. A new category is a config change, and an undeclared folder is an error.
Your own status vocabulary
Status values and the one that means “still maintained” come from the config: current/deprecated by default, draft/stable/deprecated or aktuell/veraltet if you prefer.
Reviews that come due
With review_interval_days, an active entry whose last_reviewed date is too old gets a warning. Deprecated entries stay quiet.
Secrets stay encrypted
Files in a secrets category must be SOPS-encrypted. The check is structural and never decrypts. Entries are scanned for private-key headers and API-key prefixes.
Links and attachments resolve
related links, _attachments.yml paths and their referenced_by entries must exist. http(s) URLs get a HEAD request that only warns, so flaky CI networks do not fail the build.
Generated at build time
All examples →Checking knowledge-base/secrets/example-credential.yaml Checking knowledge-base/processes/entry-review.md Checking knowledge-base/decisions/2026-01-vector-store.md Checking knowledge-base/glossary/rag.md Checking knowledge-base/glossary/chunking.md WARNING: last_reviewed is 469 days old (> 365), but the entry is marked 'current' Checking knowledge-base/_attachments.yml 3 of 4 knowledge entries are marked public. All checks passed (1 warning(s)).
A sample knowledge base with one entry past its review interval: warnings are reported, the run still exits 0. Captured from the release binary; the same output is in the showcase.
examples/output/lint-knowledge-base.txtQuickstart
Install the binary, point it at the knowledge directory, and read the report. The documentation covers the config, every rule and CI usage.
- Install knowledge-lint from crates.io with
cargo install. - Declare your categories in
knowledge/_types.yml. - Run
knowledge-lint lint; it exits 1 on any error.
# Install the binary
cargo install knowledge-lint
# Check the knowledge/ directory (the default)
knowledge-lint lint
# Or any other path
knowledge-lint lint docs/knowledge