Zenzic Shield internally audits this repository for credential leaks on every commit.
Deterministic audit of documentation structures with bidirectional traceability.
Every finding maps to a source file and a line number. Every URL has a physical origin. Zero global state.
Got a folder of Markdown files? Run an instant security and link audit using uv:
uvx zenzic check all ./your-folderZenzic will identify your engine via its configuration files or default to Standalone Mode for plain Markdown folders โ providing immediate protection for links, credentials, and file integrity.
Note: In Standalone Mode there is no declared navigation contract, so orphan-page detection (
Z402) is disabled. What you get: full link validation (Z101/Z104), credential scanning (Z201), path-traversal blocking (Z202), and directory-index integrity checks (Z401).
pip install zenzic
zenzic lab # Interactive showroom โ 9 acts, every engine, zero setup
zenzic check all # Audit the current directory๐ Full docs โ ยท ๐ Badges ยท ๐ CI/CD guide
๐ CI/CD Ready: Use the Official Zenzic Action to run Zenzic in GitHub Actions โ findings surface directly in Code Scanning, PR annotations, and the Security tab.
- uses: PythonWoods/zenzic-action@v1 with: format: sarif upload-sarif: "true"
| Without Zenzic | With Zenzic |
|---|---|
| โ Broken anchors silently 200 OK in Docusaurus v3 | โ Mathematical anchor validation via VSM |
| โ Leaked API keys in code blocks committed to git | โ The Shield โ 9-family credential scanner, exit 2 |
โ Path traversal ../../../../etc/passwd in links |
โ Blood Sentinel โ non-suppressible exit 3 |
| โ Orphan pages unreachable from any nav link | โ Semantic orphan detection โ not just file-exists |
| โ Silent 404s accumulating in Google Search Console | โ Directory Index Integrity checks |
| โ MkDocs โ Zensical migration with unknown breakage | โ Transparent Proxy โ lint both with one command |
- Not a site generator. It audits source; it never builds HTML.
- Not a build wrapper. Zero-Trust Execution: no subprocesses, no
mkdocsordocusaurusbinaries invoked. - Not a spell checker. Structure and security โ not prose.
- Not an HTTP crawler. All validation is local and file-based.
- Not a blackbox. Every finding carries a code, a source file, and a line number. Absolute traceability โ no finding without a physical origin.
| Capability | Command | Detects | Exit |
|---|---|---|---|
| Link integrity | check links |
Broken links, dead anchors | 1 |
| Circular anchors | check all |
Self-referential anchor links (Z107) |
1 |
| Orphan detection | check orphans |
Files absent from nav โ invisible after build |
1 |
| Code snippets | check snippets |
Syntax errors in Python / YAML / JSON / TOML blocks | 1 |
| Untagged code blocks | check all |
Fenced blocks with no language specifier (Z505) |
1 |
| Placeholder content | check placeholders |
Stub pages and forbidden text patterns | 1 |
| Unused assets | check assets |
Images and files not referenced anywhere | 1 |
| Config asset integrity | check all |
Favicon and OG image paths declared in engine config confirmed on disk (Z404) |
1 |
| Brand integrity | check all |
Obsolete release codenames (Z905) โ configurable via [project_metadata] |
1 |
| Credential scanning | check references |
9 credential families โ text, URLs, code blocks | 2 |
| Path traversal | check links |
System-path escape attempts | 3 |
| Enterprise reporting | check all --format sarif |
SARIF 2.1.0 output for GitHub Code Scanning โ inline PR annotations | 1/2/3 |
| Quality score | score |
Deterministic 0โ100 composite metric | โ |
| Regression detection | diff |
Score drop vs saved baseline โ CI-friendly | 1 |
Autofix: zenzic clean assets [-y] [--dry-run] deletes unused images.
๐ v0.7.1 "Quartz Maturity" (Stable) โ Z104 proactive suggestions, Standalone Mode truth audit, and Engineering Ledger hardening. See CHANGELOG.md.
Two security layers are permanently active โ neither is suppressible by --exit-zero:
The Shield scans every line โ including fenced code blocks โ for credentials. Unicode normalization defeats obfuscation (HTML entities, comment interleaving, cross-line lookback). Detected families: AWS, GitHub, GitLab PAT, Stripe, Slack, OpenAI, Google, PEM headers, hex payloads. Base64 speculative decoding catches obfuscated credentials in frontmatter and code blocks. โ Exit 2. Rotate and audit immediately.
Blood Sentinel normalizes every resolved link with os.path.normpath and rejects any path
escaping the docs/ root. Catches ../../../../etc/passwd-style traversal before any OS syscall.
โ Exit 3.
| Exit | Meaning |
|---|---|
0 |
All checks passed |
1 |
Quality issues found |
2 |
SECURITY โ leaked credential detected |
3 |
SECURITY โ system-path traversal detected |
Add
zenzic check referencesto your pre-commit hooks to catch leaks before git history.
Zenzic reads config files as plain text โ never imports or executes your build framework:
| Engine | Adapter | Highlights |
|---|---|---|
| Docusaurus v3 | DocusaurusAdapter |
Versioned docs, @site/ alias, Ghost Route detection, Virtual Routes (Tags, Pagination, Authors) |
| MkDocs | MkDocsAdapter |
i18n suffix + folder modes, fallback_to_default |
| Zensical | ZensicalAdapter |
Transparent Proxy bridges mkdocs.yml if zensical.toml absent |
| Any folder | StandaloneAdapter |
File integrity checks only โ orphan detection disabled without a nav contract |
Third-party adapters install via the zenzic.adapters entry-point group.
See the Developer Guide for the adapter API.
Docusaurus generates URL pages that have no physical Markdown source: tag listing pages
(/blog/tags/python/), paginated blog indexes (/blog/page/2/), and author pages
(/blog/authors/alice/). Before EPOCH 7b, Zenzic had no knowledge of these routes, so
any link pointing at them was incorrectly flagged as a broken link.
DocusaurusAdapter now builds a Virtual Route map derived from frontmatter metadata
โ no build step needed, no Node.js execution. Each virtual route carries a source_files
set that traces it back to the physical files that generate it, satisfying the
Reverse-Mapping Invariant: every URL in the VSM has an unambiguous physical origin.
A VirtualRoute with source_files=frozenset() raises ValueError at construction โ
no untraced URL can reach the VSM.
Zero-config by default. Full priority chain: CLI flags > zenzic.toml > [tool.zenzic] in pyproject.toml > built-ins. CLI flags always take precedence over configuration files.
# zenzic.toml (all fields optional)
docs_dir = "docs"
fail_under = 80 # exit 1 if score < threshold; 0 = observe only
excluded_dirs = ["includes", "assets", "overrides"]
excluded_build_artifacts = ["pdf/*.pdf", "dist/*.zip"]
placeholder_patterns = ["coming soon", "todo", "stub"]
[build_context]
engine = "mkdocs" # mkdocs | docusaurus | zensical | standalone
default_locale = "en"
locales = ["it"]zenzic init # Generate zenzic.toml with auto-detected values
zenzic init --pyproject # Embed [tool.zenzic] in pyproject.tomlCustom lint rules โ declare project-specific patterns in zenzic.toml, no Python required:
[[custom_rules]]
id = "ZZ-NODRAFT"
pattern = "(?i)\\bDRAFT\\b"
message = "Remove DRAFT marker before publishing."
severity = "warning"Rules fire identically across all adapters. No changes required after engine migration.
DFA Guarantee (v0.7.1+): Custom rule patterns must be RE2-compatible โ backreferences, lookaheads, and lookbehinds are rejected at load time. See Architecture โบ DFA Guarantee.
permissions:
contents: read
security-events: write # required for Code Scanning upload
steps:
- uses: actions/checkout@v6
- name: ๐ก๏ธ Zenzic Documentation Quality Gate
uses: PythonWoods/zenzic-action@v1
with:
format: sarif
upload-sarif: "true" # findings appear in the Security tab and as PR annotations- name: ๐ก๏ธ Zenzic Sentinel
run: uvx zenzic check all --strict
# Exit 1 = quality ยท Exit 2 = leaked credential ยท Exit 3 = path traversal
# Exits 2 and 3 are never suppressible.
- name: Regression gate
run: |
uvx zenzic score --save # on main branch
uvx zenzic diff # on PR โ exit 1 if score dropsFor badge automation and regression gates, see the CI/CD guide.
Full workflow: .github/workflows/ci.yml
# Zero-install, one-shot audit (recommended for CI and exploration)
uvx zenzic check all ./docs
# Global CLI tool
uv tool install zenzic
# Pinned dev dependency
uv add --dev zenzic
# pip
pip install zenzicPortability: Zenzic rejects absolute internal links (starting with /). Relative links
work at any hosting path. External https:// URLs are never affected.
Python compatibility: Zenzic requires Python 3.10+. In CI, every release is officially validated against Python 3.10 (Floor โ legacy compatibility) and Python 3.14 (Peak โ performance and future-readiness). If you run any version in between, it works.
# Checks
zenzic check links [--strict] [--no-external]
zenzic check orphans
zenzic check snippets
zenzic check placeholders
zenzic check assets
zenzic check references [--strict] [--links]
zenzic check all [--strict] [--exit-zero] [--format json] [--engine ENGINE]
zenzic check all [--exclude-dir DIR] [--include-dir DIR] [--no-external]
# Score & diff
zenzic score [--save] [--fail-under N]
zenzic diff [--threshold N]
# Autofix
zenzic clean assets [-y] [--dry-run]
# Init
zenzic init [--pyproject]
# Interactive showroom
zenzic lab [--act N] [--list]Visit the documentation portal for interactive screenshots and rich examples.
Zenzic's docs ship as two separate Docusaurus instances under the same domain. Each has its own sidebar, search, and audience โ never mixed.
zenzic.dev/
โโโ docs/ โ User Area โ install, configure, CI/CD, finding codes
โโโ developers/ โ Dev Area โ plugins, adapters, ADRs, tech debt ledger
โโโ blog/ โ Release notes & engineering post-mortems
โโโ community/ โ Brand kit, FAQs, governance
The Quartz Promise. Two instances, one Sentinel. The split is enforced by ADR 011: Cross-Instance Allowlist โ every cross-boundary link is a documented contract, never a silent suppression. Hidden debt corrupts trust; declared debt is engineering. See the Technical Debt Ledger for what we deferred and why.
Entry points:
| You are a... | Start here |
|---|---|
| ๐ค User integrating Zenzic | uvx zenzic lab ยท User Guide |
| ๐ง Contributor / plugin author | Developer Portal ยท ADR Vault |
| ๐ก๏ธ Security reviewer | Engineering Ledger ยท SECURITY.md |
Zenzic is governed by three non-negotiable operational contracts โ each enforce-able by machine, not by convention.
Three design axioms from the NASA Power of 10 โ applied without exception:
- Rule 1 / Rule 4 โ Pure, deterministic control flow. The analysis engine has zero global
state. Given identical inputs, Zenzic produces identical output. Every finding maps to a
single source file and a single line number. No stochastic components; no inference
dependencies declared in
pyproject.toml. - Rule 2 โ No dynamic execution.
subprocess.Popen,os.system, and all shell invocations are permanently banned fromsrc/. Docusaurus TypeScript configs are parsed as plain text. Node.js is never invoked. The Zero Subprocess invariant is enforced by ruff and audited at every push.
These are not conventions โ they are machine-enforced contracts.
|
Zero Assumptions โ Every adapter runs under # mypy: strict = true
# Zero untyped defs, zero ignored errors. |
Subprocess-Free โ # ruff: ban = ["subprocess"]
# Deterministic static analysis only. |
Deterministic Compliance โ Every source file carries an SPDX header. REUSE 3.x is enforced in CI. No ambiguous licensing โ machine-verifiable on every PR. # REUSE-IgnoreStart / REUSE-IgnoreEnd
# SPDX-License-Identifier: Apache-2.0 |
See the Architecture Guide for the Two-Pass Reference Pipeline and VSM deep-dive.
Why not grep? Grep is blind to structure. Zenzic understands Docusaurus versioning,
MkDocs i18n fallbacks, and Ghost Routes โ pages that don't exist as files but are valid URLs.
Does it run my build engine? No. 100% subprocess-free. Static analysis on plain text only.
Can it handle thousands of files? Yes. Adaptive parallelism for discovery; O(1) VSM lookup
per link; content-addressable cache (SHA256(content + config + vsm_snapshot)) skips unchanged files.
Shield vs Blood Sentinel? Shield = secrets inside content (exit 2). Blood Sentinel = links pointing to OS system paths (exit 3). Both are non-suppressible.
No zenzic.toml needed? Correct. Zenzic identifies the engine from config files present and applies safe defaults. Run
zenzic init at any time to generate a pre-populated config file.
What is zenzic lab? A 9-act interactive showroom covering every engine and error class.
Run it once before integrating Zenzic into any project.
uv sync --all-groups
nox -s tests # pytest + coverage
nox -s lint # ruff
nox -s typecheck # mypy --strict
nox -s preflight # lint + format + typecheck + pytest + reuse
just verify # preflight + zenzic check all --strict (self-dogfood)See the Contributing Guide for the Zenzic Way checklist and PR conventions.
- Open an issue to discuss the change.
- Read the Contributing Guide โ Zenzic Way checklist, pure functions, no subprocesses, source-first.
- Every PR must pass
nox -s preflightand include REUSE/SPDX headers on new files.
See also: Code of Conduct ยท Security Policy
A CITATION.cff is present at the root. Click "Cite this repository" on
GitHub for APA or BibTeX output.
Apache-2.0 โ see LICENSE.
Zenzic was born from a technical journey through the fragility of modern documentation ecosystems. Discover the philosophy, the security siege, and the engineering behind the Sentinel in the Engineering Chronicles on the official blog.
The v0.7.1 release story โ AI-driven red-team siege, 4 bypass vectors closed, and the road to engine-agnostic parity โ is documented in Beyond the Siege: Zenzic v0.7.1.
Engineered with precision by PythonWoods in Italy ๐ฎ๐น
"Building the Safe Harbor for technical knowledge."
Documentation ยท GitHub ยท Blog