Generic, lightweight semantic-tainting static analyzer for Python — track untrusted data across your codebase and gate trust-boundary violations, with zero runtime dependencies.
# demo.py
from weft_markers import trusted, external_boundary
@external_boundary
def read_request(req):
return req.body # raw, untrusted (EXTERNAL_RAW)
@trusted(level="ASSURED")
def build_record(req):
return read_request(req) # claims ASSURED, returns raw — no validation$ wardline scan . --fail-on ERROR
scanned 1 file(s); 3 finding(s) — 0 suppressed (0 baseline / 0 waiver / 0 judged), 1 active -> findings.jsonl
$ echo $?
1The gate trips (exit 1) and the findings land in findings.jsonl (JSON Lines;
--format sarif for GitHub code scanning). Wardline is agent-first — you don't
read that file by hand. Your coding agent does: ask it "why did the scan fail?"
and it surfaces the one active defect (the other two findings are NONE-severity
engine facts):
demo.build_recorddeclares return trustASSUREDbut actually returnsEXTERNAL_RAW(less trusted) — untrusted data reaches a trusted producer.demo.py:8·PY-WL-101
Wardline reads your Python statically — it never runs your code — and asks one question of every trust-annotated function: is the data this function works with as trusted as it claims? It tracks a taint (a trust level) for every value and propagates it across the whole project, flagging the places where untrusted data reaches a trusted producer with no validation in between.
Wardline is part of Weft — an agent-first suite of small, local-first
developer tools, each driven by a coding agent as much as a person, giving small
teams capable tooling without enterprise weight. The authoritative federation
hub, roster, and composition doctrine live at ~/weft (see
~/weft/doctrine.md); rather than restate membership here, refer to the hub for
the current roster and the enrich-only axiom that governs how the tools compose.
Opt-in by design. Wardline is silent until you opt in. Undecorated code sits in the developer-freedom zone — unknown-trust, no findings. You declare trust on the functions that matter, and only then does Wardline enforce it. That is what lets it scan a large untouched codebase (including its own) with zero noise.
- Deterministic whole-program taint — function-, variable-, and project-level analysis over an inter-module call graph; no runtime instrumentation.
- Opt-in trust model — three decorators (
@external_boundary,@trust_boundary,@trusted) mark your boundaries; the engine infers the rest. - Four policy rules — untrusted-reaches-trusted, non-rejecting boundary, broad exception handler, and silently-swallowed exception.
- Zero-dependency base —
pip install wardlinepulls nothing; functionality lives behind small extras. - Structured output — JSONL, SARIF (generic interchange/GitHub code-scanning), and native Filigree emit for finding lifecycle work.
- Agent-native —
wardline mcpis a dependency-free MCP-over-stdio server;wardline installwires Wardline into your coding agent in one command. - Opt-in LLM triage —
wardline judgelabels findings TRUE/FALSE positive (dependency-free; never runs automatically). - Light-touch suppression — baselines and time-boxed, reasoned waivers.
- Loomweave integration — persist per-entity taint facts to a Loomweave store.
pip install 'wardline[scanner]' # quote the extras for zsh# app.py
from weft_markers import trusted, external_boundary
@external_boundary
def read_request(req):
return req.body
@trusted(level="ASSURED")
def build_record(req):
return read_request(req)wardline scan . --fail-on ERROR # exit 0 = clean, 1 = gate tripped, 2 = wardline errorFix findings at the boundary (validate before returning), not at the sink.
pip install weft-markers # tiny runtime marker package for application code
pip install wardline # zero-dependency base (library + decorators)
pip install 'wardline[scanner]' # the scan/judge/baseline CLI + MCP server (quote for zsh)Prefer weft_markers in application code. Wardline still recognizes
wardline.decorators for backward compatibility and direct Wardline users, but
weft-markers is the neutral marker-only runtime dependency.
| Extra | Pulls | Enables |
|---|---|---|
scanner |
pyyaml, jsonschema, click | the wardline CLI and wardline mcp server |
loomweave |
blake3 | persisting taint facts to a Loomweave store |
docs |
mkdocs, mkdocs-material | building the documentation site |
The LLM triage judge (wardline judge) is dependency-free (stdlib urllib →
OpenRouter) and needs no extra.
wardline installThis injects a hash-fenced instruction block into CLAUDE.md/AGENTS.md,
installs the wardline-gate skill, merges a wardline entry into .mcp.json,
writes Codex's ~/.codex/config.toml MCP entry, and records Loomweave/Filigree
bindings if present. When local sibling config exposes a URL, install wires it
directly; otherwise it leaves a commented stanza to fill in. Agents then run the
scan → explain → fix-at-boundary → rescan loop natively. The wardline mcp
server exposes scan, explain_taint, fix, judge, baseline, and waiver
tools over JSON-RPC with no SDK.
wardline install also reminds application projects to install weft-markers
and import from weft_markers when they want runtime-importable trust markers
without depending on the full Wardline scanner package.
Run wardline doctor to check those artifacts later, or wardline doctor --repair to refresh stale/missing wiring after moving tools or starting a
Filigree dashboard.
Use Wardline when you want a deterministic, opt-in trust-boundary gate you can run in CI and hand to an agent — lightweight, Python-native, no external service.
It is not the right tool when you need:
- Full interprocedural everything. Wardline is precise at the function and project-call-graph level (L1–L2 with an L3 fixed point), not an exhaustive, path-sensitive whole-program prover.
- A broad SAST suite. Wardline checks trust boundaries and a small set of exception-handling rules; it is not a replacement for a general-purpose scanner that covers dozens of vulnerability classes.
- Non-Python code. Wardline analyzes Python ≥3.12 only.
- Zero-config coverage. Wardline is silent until you declare trust — that is the point, but it means it finds nothing on an un-annotated codebase.
Full documentation lives at https://foundryside-dev.github.io/wardline/.
| Document | Description |
|---|---|
| Getting Started | Install, decorate, first scan |
| Taint & Trust Model | The lattice, decorators, and propagation |
| Rules | The four policy rules |
| Configuration | weft.toml [wardline]: rules, severity, excludes |
| Suppression | Baselines and waivers |
| LLM Triage Judge | Opt-in TRUE/FALSE-positive labelling |
| Loomweave Taint Store | Persisting taint facts |
| CLI Reference | Every command and flag |
| Trust Vocabulary | The decorators and their arguments |
| Agent Integration | Using Wardline from a coding agent |
Requires Python ≥3.12. Developed on 3.13 with uv.
git clone https://github.com/foundryside-dev/wardline
cd wardline
uv sync --all-extras --group dev
make ci # ruff check + format check + mypy strict + pytest (90% coverage floor)
make lint # ruff check + format --check
make format # auto-format and fix
make typecheck # mypy strict
make test # pytest
make scan-self # dogfood: scan wardline's own sourceSee CONTRIBUTING.md for the full workflow and CLAUDE.md for the developer architecture guide.
Wardline is one of the Weft tools (with Loomweave and Filigree) — an agent-first, local-first developer tooling suite for small teams.
MIT — Copyright (c) 2026 John Morrissey