Skip to main content

Local Sentinel Gate

Don't debug the build output. Fix the source before the build starts.

A documentation error discovered in CI means a failed pipeline, a context switch, and a wasted build minute. Discovered before the build, it is just a one-line fix.

The Sentinel Gate pattern closes the gap: Zenzic runs as a mandatory pre-step, blocking the build command if the source is not clean. No findings — no gate — no wasted cycle.


The Principle

The Sentinel Gate enforces a simple invariant:

zenzic check all [PATH] --strict → success → your build tool
→ failure → build blocked

The gate fires locally — before you push, before CI sees the branch. It is the same analysis that runs in your GitHub Actions workflow, applied at the moment when fixing it is cheapest.


Recipes by Ecosystem

Pick the recipe that matches your build toolchain.

Docusaurus projects use npm run build. Gate it in package.json:

package.json
{
"scripts": {
"zenzic": "uvx zenzic check all . --strict",
"build": "npm run zenzic && docusaurus build"
}
}

npm run build now runs Zenzic first. If any finding is detected, docusaurus build never starts. Your existing CI command (npm run build) gains the gate automatically — no changes to the workflow YAML needed.

Pinned version in production

Replace uvx zenzic with uvx "zenzic==0.7.0" for deterministic CI. On the first warm install, uv caches the wheel — subsequent runs are near-instant.


Why Gate Locally, Not Only in CI

Discovery pointCost to fix
Before the build (local gate)Seconds — the editor is still open
CI pipelineMinutes — push, wait, read log, fix, re-push
Production deployHours — rollback, triage, hotfix

The Sentinel Gate shifts discovery to the cheapest possible moment. By the time CI runs, the documentation is already clean — CI becomes a confirmation rather than a detector.


Exit Code Reference

CodeMeaningGate behaviour
0All checks passedBuild proceeds
1Quality findings (links, orphans, placeholders)Build blocked by default; add --no-fail-under to allow
2Shield breach — credential detectedAlways blocked. Never suppressible.
3Blood Sentinel — system path traversalAlways blocked. Never suppressible.

Exit codes 2 and 3 are unconditional stops. No flag or configuration can suppress a security incident.


Pre-Launch and Staging Environments

External links to sites that are not yet public — documentation domains, GitHub release tags, staging URLs — return HTTP 404 until the deploy completes. The Sentinel Gate blocks the build on these, which is correct behaviour: a broken external link is a real finding.

When you are deliberately building documentation before the target site goes live, instruct the gate to skip external checks for that run using ZENZIC_EXTRA_ARGS:

# Skip all external link checks — pre-launch or network-restricted environments
ZENZIC_EXTRA_ARGS="--no-external" just build

# Exclude one specific pre-launch domain, keep all other external checks active
ZENZIC_EXTRA_ARGS="--exclude-url https://zenzic.dev/" just build

ZENZIC_EXTRA_ARGS is an environment variable read by both just sentinel and just build. It injects flags into the Zenzic invocation without modifying zenzic.toml or the justfile — the source of truth for configuration remains unchanged. Unset, it expands to empty and the gate behaves at full strictness.

Explicit exception, not a new default

ZENZIC_EXTRA_ARGS must be set explicitly on each invocation. It is not persisted in any configuration file. Run just build without the variable to confirm that the gate still blocks on the broken links:

just build
# ✘ [EXTERNAL_LINK] blog/example.mdx:12: 'https://zenzic.dev/blog/' returned HTTP 404
# FAILED: Hard errors detected. Exit code 1 is mandatory.

The protection is active by default. The variable is an operator exception, not a configuration change.

docs/guides/setup.md:14Z101Broken link → 'install.md' (target not found)
docs/guides/old-api.mdZ402Orphan page — not reachable from any navigation
docs/reference/config.md:3Z501Placeholder: "TODO: describe this parameter"
2 errors1 warningScore: 67 / 100

The finding above is what a pre-launch external link looks like. It is accurate — the URL does not resolve. ZENZIC_EXTRA_ARGS="--no-external" suppresses it for one build invocation only.