Generic, lightweight semantic-tainting static analyzer for trust boundaries. Wardline's full analyzer targets Python; its Rust preview catches command-injection defects over crate-aware identity. The base package has zero runtime dependencies, and scanner/front-end functionality stays behind opt-in extras.
# 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 -> .wardline/20260620T153012Z-findings.jsonl
$ echo $?
1The gate trips (exit 1) and the findings land in timestamped JSON Lines under
.wardline/ by default (--output PATH writes to an exact path; --format sarif emits 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 code statically — it never runs it — and asks one question
of every trust-annotated boundary: is the data this function works with as
trusted as it claims? For Python, it tracks a taint (a trust level) for
values through function bodies and the project call graph, flagging places where
untrusted data reaches a trusted producer with no validation in between. For
Rust, the preview frontend currently focuses on command-injection sinks around
std::process::Command.
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 Python taint analysis — function-, variable-, and project-level analysis over an inter-module call graph; no runtime instrumentation.
- Rust command-injection preview —
wardline scan --lang rustfindsRS-WL-108/RS-WL-112over.rstrees with crate-prefixed, baseline-eligible finding identity. - Opt-in trust model — Python decorators (
@external_boundary,@trust_boundary,@trusted) and Rust doc-comment markers declare the boundary surface; undecorated code stays quiet. - Trust-boundary and sink rules — boundary-integrity rules, exception-flow rules, and expanded sink families for command execution, dynamic code/imports, deserialization, path traversal, SSRF, SQL injection, XML parsing, templates, native library loads, logging format strings, and SMTP sends.
- Zero-dependency base —
pip install wardlinepulls nothing; scanner, Loomweave, Rust, and docs functionality live behind small extras. - Structured output — JSONL, SARIF, agent-summary JSON, signed legis scan artifacts, and native Filigree emission for finding lifecycle work.
- MCP-primary agent surface —
wardline mcpis a dependency-free MCP-over-stdio server with structured tool output, schema declarations, and tools for scan, explain, fix, judge, doctor, rekey, assurance, attestation, dossier, and finding lifecycle work. - Reproducible evidence and migrations —
assure,attest, andrekeyreport trust-surface coverage, sign reproducible posture bundles, and migrate fingerprint-keyed stores across scheme changes. - Opt-in LLM triage —
wardline judgelabels findings TRUE/FALSE positive (dependency-free; never runs automatically). - Light-touch suppression — baselines, time-boxed waivers, and judged findings with explicit gate semantics.
- 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)
pip install 'wardline[rust]' # Rust command-injection preview frontendPrefer 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 |
rust |
scanner extra, tree-sitter, tree-sitter-rust | wardline scan --lang rust |
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,
and writes Codex's ~/.codex/config.toml MCP entry. Agents then run the scan →
explain → fix-at-boundary → rescan loop natively. The wardline mcp server
exposes the primary tool surface over JSON-RPC with no SDK, including scan,
filtered findings, explain-taint, fix, judge, baseline/waiver, doctor, rekey,
assure, attest, dossier, and Filigree filing tools.
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.
Wardline reads operator configuration from the [wardline] table in
weft.toml. Machine-written state lives under .weft/wardline/: baselines,
waivers, judged findings, and cache data stay out of the authored config file.
Sibling URLs are resolved at runtime from flags, environment variables, or
published local Weft port files. wardline install and wardline doctor detect
sibling tools such as Filigree and Loomweave, but they do not persist endpoint
bindings into project config.
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.
- Full non-Python coverage. Wardline's Rust frontend is a preview for command-injection findings only; it is not a general Rust SAST engine.
- Zero-config coverage. Wardline is silent until you declare trust — that is the point, but it means it finds nothing meaningful 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 boundary, exception-flow, and sink rules |
| Configuration | weft.toml [wardline]: rules, severity, excludes |
| Suppression | Baselines and waivers |
| LLM Triage Judge | Opt-in TRUE/FALSE-positive labelling |
| Rust Support | Preview Rust command-injection frontend |
| Weft Integration | SARIF, Filigree, Loomweave, and sibling URL resolution |
| Assurance Posture | Coverage posture, attestations, and trust-surface evidence |
| 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