Skip to content

Repository files navigation

Driftlock

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


Table of Contents

  1. How It Works
  2. What Counts as a Structural Change
  3. Installation
  4. Quick Start
  5. Configuration
  6. LLM Providers
  7. Commands
  8. Behavior & Workflow
  9. Continuous Integration
  10. Ignoring Symbols
  11. Caching & Cost
  12. Audit Trail (Solana)
  13. Escaping the Hook
  14. Development
  15. License

Full documentation lives in the docs/ folder: Getting Started · Tutorial · Configuration · CI/CD · Providers · Ignoring · Caching · Architecture · Troubleshooting


How It Works

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.


What Counts as a Structural Change

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.


Installation

Unix (Linux & macOS)

curl -fsSL https://raw.githubusercontent.com/Ksschkw/driftlock/main/install.sh | sh

Windows

Open PowerShell and run:

Set-ExecutionPolicy Bypass -Scope Process -Force
iwr https://raw.githubusercontent.com/Ksschkw/driftlock/main/install.ps1 -UseBasicParsing | iex

Via Go

go install github.com/Ksschkw/driftlock/cmd/driftlock@latest

Manual download

Grab the correct binary for your platform from the Releases page.


Quick Start

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.

Configuration

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

[[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 *.md files inside.

llm

[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 expansion
  • driver – adapter: openai-compatible (OpenRouter, Groq, DeepSeek, Together, vLLM) or ollama.
  • endpointfull 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 .env from the project root, so api_key = "${DRIFTLOCK_API_KEY}" resolves from .env without exporting it each shell. Ollama needs no key. Since the secret stays in .env, commit .driftlock.toml so your team shares one policy (driftlock init sets 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

[behavior]
auto_fix = true
block_on_false = true
block_on_llm_error = false
max_retries = 2
include_full_diff = false
cache = true
  • auto_fix – if true, rewrites documentation when drift is detected.
  • block_on_false – if true, aborts the commit when docs are outdated.
  • block_on_llm_error – if true, aborts the commit when the LLM is unreachable.
  • max_retries – number of retries with exponential backoff.
  • include_full_diff – if true, sends the complete git diff to the LLM (uses more tokens).
  • cache – if true (default), caches verdicts so identical checks never re‑bill the LLM. See Caching & Cost.

audit

[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.


LLM Providers

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).


Commands

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.


Behavior & Workflow

When you run git commit:

  1. If no mapped source files are staged, Driftlock exits silently.
  2. If mapped sources have no structural changes, a green message is printed.
  3. 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 = true to change that.

Use DRIFTLOCK_DEBUG=1 to see the raw LLM payloads and responses (including token usage).


Continuous Integration

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 Action

# .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 rollout

Any CI system

driftlock check --base "$BASE_SHA" --head "$HEAD_SHA" --json

pre‑commit framework

Driftlock 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: driftlock

Full guide: docs/ci-cd.md.


Ignoring Symbols

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.


Caching & Cost

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.


Escaping the Hook

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.


Audit Trail (Solana)

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.


Development

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.


License

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


About

Driftlock is a commit-time gatekeeper that detects when your documentation has fallen behind your code—and then fixes it for you.

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages