Driftlock is a commit‑time gatekeeper that ensures your documentation never
falls behind your code. It watches every git commit, detects when a
structural change to your code (function signatures, class methods, exported
types) is not reflected in your Markdown docs, and either blocks the commit
or automatically rewrites the documentation to match. No more "I'll update
the docs later" – Driftlock makes stale documentation a build failure.
Optionally, Driftlock can log an immutable audit trail to a Solana devnet contract, giving you cryptographic proof that documentation matched code at the moment of every commit.
Example usage on youtube: https://youtu.be/o-Ox7lnqHxs
- How It Works
- What Counts as a Structural Change
- Installation
- Quick Start
- Configuration
- LLM Providers
- Commands
- Behavior & Workflow
- Continuous Integration
- Ignoring Symbols
- Caching & Cost
- Audit Trail (Solana)
- Escaping the Hook
- Development
- License
Full documentation lives in the
docs/folder: Getting Started · Tutorial · Configuration · CI/CD · Providers · Ignoring · Caching · Architecture · Troubleshooting
git commit
│
▼
.git/hooks/pre-commit → driftlock hook-run
│
▼
1. Capture staged diff (git diff --cached)
2. Parse old & new file versions to extract structural signatures
3. Map changed files → Markdown documents (from .driftlock.toml)
4. If structural changes exist:
a. Extract only the relevant sections of the documentation (smart chunking)
b. Send (diff + chunked doc) to an LLM
c. LLM returns TRUE (docs OK) or FALSE (outdated)
d. If FALSE → LLM rewrites the affected sections, which are then
merged back into the full document. Commit is blocked.
5. If no structural changes: print a green status message, exit 0
6. (Optional) Log SHA-256 hash to local audit file or Solana
Driftlock is not a simple text‑diff checker. It only triggers when the API‑visible surface of your code changes. It stays completely silent for cosmetic changes, comment edits, or internal refactors that don't alter public signatures.
Driftlock parses source in any language and recognizes structural elements:
- Functions/methods — Go, Python, JavaScript/TypeScript, Java, C#, C/C++, Rust, Swift, Kotlin, Scala, PHP, Ruby, Shell, Lua, Clojure, and more.
- Classes / interfaces / structs / traits / enums definitions.
- Type declarations (TypeScript, Go, Rust, etc.).
- SQL table/view/procedure definitions.
- YAML/JSON keys, XML/HTML tags, INI/TOML sections, Markdown headings — for the corresponding data/markup file types.
The parser dispatches by file extension — a .go file is only matched with
Go patterns, never YAML or Markdown ones — and it strips comments and string
literals before matching, so a signature that appears inside a comment or a
string never produces a phantom change. Multi‑line signatures are supported.
If the signature changes (added/removed parameter, different return type, renamed function), Driftlock will trigger. Adding a new function triggers it as well, even if the docs never mentioned it before – Driftlock will ask the LLM to create appropriate documentation. A rename shows up as one removed plus one added symbol.
Non‑triggers: modifying a function body, renaming a local variable, adding
a comment, or changing whitespace/formatting. You can also explicitly exclude a
symbol with a driftlock:ignore annotation.
If you want Driftlock to see the full diff (including bodies and comments)
when structural changes are present, enable include_full_diff = true in
your config. This gives the LLM more context to write richer documentation.
curl -fsSL https://raw.githubusercontent.com/Ksschkw/driftlock/main/install.sh | shOpen PowerShell and run:
Set-ExecutionPolicy Bypass -Scope Process -Force
iwr https://raw.githubusercontent.com/Ksschkw/driftlock/main/install.ps1 -UseBasicParsing | iexgo install github.com/Ksschkw/driftlock/cmd/driftlock@latestGrab the correct binary for your platform from the Releases page.
cd your-project
driftlock init # interactive guided setup
# edit .driftlock.toml to set your LLM provider and API key
git add . && git commit -m "your message"
# If your docs are out of sync, the commit is blocked and the docs are updated.Driftlock looks for .driftlock.toml in your Git repository root. All fields
have sensible defaults; you only need to set your LLM provider.
[[doc_mapping]]
sources = ["cmd/**", "internal/**"]
docs = ["README.md"]sources– glob patterns matching source files.**matches all subdirectories.docs– Markdown files or directories. Directories are expanded to*.mdfiles inside.
[llm]
driver = "openai-compatible" # or "ollama"
endpoint = "https://api.openrouter.ai/api/v1/chat/completions"
model = "deepseek/deepseek-chat"
api_key = "${DRIFTLOCK_API_KEY}" # env var expansiondriver– adapter:openai-compatible(OpenRouter, Groq, DeepSeek, Together, vLLM) orollama.endpoint– full URL of the chat completions endpoint. Nothing is appended.model– model name as the provider expects it.api_key– use${ENV_VAR}to reference an environment variable. Driftlock loads a gitignored.envfrom the project root, soapi_key = "${DRIFTLOCK_API_KEY}"resolves from.envwithout exporting it each shell. Ollama needs no key. Since the secret stays in.env, commit.driftlock.tomlso your team shares one policy (driftlock initsets this up for you).options– extra parameters passed directly to the API (temperature,max_tokens, etc.).[llm.prompts]– optional override of the built‑in check and fix prompts.
[behavior]
auto_fix = true
block_on_false = true
block_on_llm_error = false
max_retries = 2
include_full_diff = false
cache = trueauto_fix– iftrue, rewrites documentation when drift is detected.block_on_false– iftrue, aborts the commit when docs are outdated.block_on_llm_error– iftrue, aborts the commit when the LLM is unreachable.max_retries– number of retries with exponential backoff.include_full_diff– iftrue, sends the completegit diffto the LLM (uses more tokens).cache– iftrue(default), caches verdicts so identical checks never re‑bill the LLM. See Caching & Cost.
[audit]
solana = false
rpc_endpoint = "https://api.devnet.solana.com"
keypair_path = "~/.config/solana/id.json"
program_id = ""When solana = true, Driftlock submits each check’s hash to the Solana
blockchain using the built‑in Memo program.
Driftlock uses an adapter system. The openai-compatible driver works with
any service that exposes an OpenAI‑style chat completions endpoint, including:
- OpenRouter – one API key, hundreds of models.
- Groq – ultra‑fast inference.
- DeepSeek – cheap, powerful models.
- Together AI
- Self‑hosted vLLM, Ollama with an OpenAI‑compatible wrapper, etc.
The ollama driver speaks the native Ollama API (default
http://localhost:11434/api/generate).
| Command | Description |
|---|---|
driftlock init |
Interactive guided setup (config, hook, .gitignore) |
driftlock hook-run [--no-fix] |
Called by the pre‑commit hook |
driftlock check [--base REF] [--head REF] [--report] [--json] |
Check for drift; never modifies files. --base enables CI range mode |
driftlock fix |
Force regeneration of all mapped documentation |
driftlock log |
Show the last 20 audit log entries |
driftlock status |
Show current status |
driftlock check runs against the staged index by default (identical to the
hook). Pass --base (and optionally --head) to compare two Git refs instead —
this is how Driftlock runs in CI against a pull request. It exits non‑zero on
drift (so it can gate a merge) unless --report is given, and can emit a
machine‑readable report with --json. See Continuous Integration.
When you run git commit:
- If no mapped source files are staged, Driftlock exits silently.
- If mapped sources have no structural changes, a green message is printed.
- If structural changes are found:
- Only the relevant sections of the documentation are sent to the LLM (smart chunking).
- If the docs are up‑to‑date (LLM returns TRUE), a green message is printed and the commit proceeds.
- If the docs are outdated (FALSE), a red message is printed. If
auto_fix = true, the LLM rewrites the affected sections, they are merged back into the full document, and the commit is blocked so you can review and stage the updated doc. - If the LLM is unreachable, a yellow warning is shown. By default the
commit proceeds; set
block_on_llm_error = trueto change that.
Use DRIFTLOCK_DEBUG=1 to see the raw LLM payloads and responses (including
token usage).
Local hooks are per‑developer and easily bypassed. To enforce docs‑in‑sync for the whole team, run Driftlock in CI against every pull request. In CI it runs in range mode (comparing two Git refs) and never modifies files — it only passes or fails.
# .github/workflows/driftlock.yml
name: Documentation Drift
on: pull_request
jobs:
driftlock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # both refs must be present
- uses: Ksschkw/driftlock@main
with:
api-key: ${{ secrets.DRIFTLOCK_API_KEY }}
report-only: 'false' # 'true' to warn without failing during rolloutdriftlock check --base "$BASE_SHA" --head "$HEAD_SHA" --jsonDriftlock ships a pre-commit hook. Add to your
.pre-commit-config.yaml:
repos:
- repo: https://github.com/Ksschkw/driftlock
rev: v0.3.0
hooks:
- id: driftlockFull guide: docs/ci-cd.md.
Not every signature belongs to your public contract. Exclude a specific
declaration from analysis with a driftlock:ignore comment — it works in any
language's comment syntax, either inline or on the line above:
func internalHelper(x int) {} // driftlock:ignore
// driftlock:ignore
func alsoInternal() {}An inline marker ignores only that declaration; a standalone comment
line ignores the declaration immediately below it. To exclude whole areas of the
codebase, scope your [[doc_mapping]] sources more narrowly. Full guide:
docs/ignoring.md.
The LLM call is the dominant cost of running Driftlock. Because a verdict is a
pure function of (model, structural diff, documentation), Driftlock caches
verdicts in .driftlock/cache.json and never re‑bills the LLM for an identical
check — across commit amends, rebases, and CI re‑runs. Combined with
structural‑only diffs and smart chunking (only the relevant doc sections
are ever sent), this keeps token usage minimal.
The cache is on by default; disable it with cache = false under [behavior].
Delete .driftlock/cache.json to clear it. Full guide:
docs/caching.md.
If you need to commit without triggering Driftlock (e.g., for bulk infrastructure changes), set the environment variable:
DRIFTLOCK_SKIP=true git commit -m "your message"This bypasses all checks and allows the commit to proceed immediately.
When enabled, Driftlock generates a SHA‑256 hash of the code diff and documentation content, then submits it to the Solana blockchain using the standard Memo Program. This hash is permanently recorded and publicly verifiable.
Requirements: Go 1.24+
git clone https://github.com/Ksschkw/driftlock.git
cd driftlock
go build -o driftlock ./cmd/driftlock
go test ./... # run the test suite
go test ./internal/parser -run TestNoFalsePositivesFromCommentsAndStrings -v
# To test locally, add the build directory to your PATH or run
# sudo cp driftlock /usr/local/bin/See docs/architecture.md for the package layout and design invariants.
Driftlock is licensed under the Business Source License 1.1 (BUSL‑1.1). You may use, modify, and distribute the software freely for any non‑commercial purpose, including personal use and internal use within an organisation.
Hosting Driftlock as a service (SaaS) or building a directly competing product requires a separate commercial license.
The BUSL‑1.1 will automatically become MIT on 2099‑12‑31.
If you need a commercial license for a prohibited use case, contact
kookafor893@gmail.com.
Full license text: LICENSE