knowledge-lintv0.1.0

Secrets and attachments

Encrypted secrets stay encrypted, plaintext keys are caught outside of them, and large originals are referenced from _attachments.yml instead of being embedded.

Encrypted secrets

A category with kind: sops_secret holds credentials encrypted with SOPS. knowledge-lint checks every file in it structurally: the file must parse as YAML and contain a top-level sops: key, which SOPS adds to everything it encrypts.

knowledge-lint never decrypts anything and never needs a private key, so the check runs in CI without access to secrets.

# encrypt a file for your own age key before committing it
sops -e --age <your-age-public-key> api-token.yaml > knowledge/secrets/api-token.yaml

A file committed without encryption is an error:

Checking broken-knowledge-base/secrets/api-token.yaml
  ERROR: file in a sops_secret category has no `sops:` metadata field — not SOPS-encrypted?

The same category also reports files that are not a YAML mapping or not valid YAML. The check cannot tell for whom a file was encrypted, only that it was.

Plaintext secrets in entries

Every Markdown entry is scanned for key material that ended up outside the secrets category.

Marker Severity
-----BEGIN PRIVATE KEY----- error
-----BEGIN OPENSSH PRIVATE KEY----- error
-----BEGIN RSA PRIVATE KEY----- error
AGE-SECRET-KEY-1 error
Words starting with sk-, ghp_, gho_, AKIA, xoxb-, xoxp- and more than 8 characters longer than the prefix warning

Key headers are unambiguous, so they fail the run. The prefixes can match ordinary identifiers, so they only warn:

Checking broken-knowledge-base/glossary/embedding.md
  WARNING: possible API key with prefix 'sk-' found — check whether this is a real secret

The scan is best effort. It is a safety net, not a replacement for a dedicated secret scanner.

Large files

A Markdown entry larger than 500,000 bytes gets a warning suggesting a reference via _attachments.yml. Change the threshold with --max-file-size <bytes>.

_attachments.yml

Large originals (scans, exports, recordings) belong outside the entry text. An entry holds the summary; _attachments.yml at the root of the knowledge directory points to the original. The file is optional.

attachments:
  - id: load-profile
    path: "./assets/load-profile.txt"
    description: "Load profile from the architecture review, kept as the original file"
    referenced_by:
      - decisions/2026-01-vector-store.md
  - id: review-recording
    path: "nas://archive/reviews/2026-01-15.mp4"
    description: "Recording of the architecture review on the office NAS (not checked from CI)"
    referenced_by:
      - decisions/2026-01-vector-store.md

Both path and referenced_by are relative to the knowledge directory.

Field Check
id Empty is an error.
description Empty or missing is a warning.
path starting with http:// or https:// A HEAD request. Unreachable is a warning, because network conditions in CI vary.
path starting with smb://, afp:// or nas:// Not checked; a CI runner cannot reach network shares.
any other path Must exist on disk. Missing is an error.
referenced_by entries Must exist on disk. Missing is an error.
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

HTTP checks are the only network access knowledge-lint makes. Without http(s) attachments, a run needs no network at all.

Edit this page on GitHub · Docs for v0.1.0