Why curfew over tach?¶
tach is excellent, but its graph
visualisation has two options that don't fit a locked-down bank/regulatory
environment:
tach show --webuploads your module structure to a remote web viewer.- The DOT output needs the GraphViz binary installed to render.
curfew is local by construction:
- No network, ever. No telemetry, no remote rendering, no "phone home". This is the headline differentiator.
- Zero runtime dependencies.
uv tool install curfewpulls in nothing — the engine and CLI run on the standard library alone. Colour is an optional[rich]extra. - No mandatory binaries. The default graph output is Mermaid, which renders in GitHub and MkDocs/Zensical with nothing installed. DOT is offered for those who already have GraphViz, but is never required.
- One tool, both checks. Module boundaries and workspace/dependency validation share one import graph.
What it checks¶
The engine builds one static import graph (via ast, never executing your code),
classifies every edge, and runs two rule-sets:
| Check | Finds | Severity |
|---|---|---|
| Module boundary | a module importing one it isn't allowed to | error |
| importing a deprecated dependency | warning | |
| bypassing a module's public interface | error | |
| External / workspace | importing an undeclared third-party distribution | error |
| workspace leakage (resolves only via a sibling's dep) | error | |
| a declared dependency that is never imported | warning | |
| an import that is neither stdlib, first-party, nor installed | warning |
How leakage detection works¶
In a uv workspace, every member shares one resolved virtual environment. That
makes it easy for member web to import requests and have it work — even
though only sibling core declared requests. The day core drops it,
web breaks. curfew flags that as ext.leak: imported, not declared by me,
but declared by a sibling. The distribution↔import-name mapping uses
importlib.metadata.packages_distributions() exclusively, so import yaml is
correctly attributed to PyYAML, sklearn to scikit-learn, and so on.