Local & air-gapped

Nothing about ci-doctor requires CI, and nothing about it requires the public internet. Both facts come from the same design rule: no runtime downloads, ever.

On your laptop

The fastest way to test a .ci-doctor.yml or reproduce an attribution you disagree with:

# Replay a log you saved from a failed job — no network, no token, no LLM:
ci-doctor analyze ~/Downloads/failing-job.log

# Or hit a live run from your checkout. With no CI_PROJECT_ID / GITHUB_REPOSITORY
# set, the repo is read from your git origin (and it says so in a warning):
ci-doctor analyze 123456

Passing a path that exists means no fetch is attempted at all — seeFirst run .

Ship it as an image

Build a self-contained image and push it to an internal registry:

# Build once where there is internet, push to your internal registry:
docker build -t ci-doctor:latest .
docker tag  ci-doctor:latest registry.internal.example.com/ci-doctor:latest
docker push registry.internal.example.com/ci-doctor:latest

# The image needs no network at job runtime beyond your GitLab/LLM endpoints.

…or as an offline wheel bundle

Where Docker isn't an option:

# Where there IS internet — build a wheel bundle:
pip wheel . -w ./wheels

# Copy ./wheels inside the air-gapped network, then:
pip install --no-index --find-links ./wheels ci-doctorr

What it still needs to reach

Nothing else. No telemetry, no update checks, no model or rule downloads, no hardcoded hosts. The one thing to avoid in a strict air gap is the litellm backend: it may pull tiktoken, which fetches its vocabulary at runtime.Requirements  has the full network story.