downscope
Least-privilege audit for Claude Code permissions — derived from what your agent actually does, not from what you guessed it needs.
Your ~/.claude/projects directory already records every tool call your agent ever made. downscope reads that history together with your merged permission settings (user / project / local layers) and answers the questions nobody can answer by hand:
- USED — which allow rules your sessions actually exercised (counts + last-seen dates)
- UNUSED — which grants are dead weight and can be tightened
- MISSING — which commands you approve by hand all the time and deserve a rule
- RISK — ~30 deterministic checks over both your config (wildcard Bash allows, secrets-adjacent grants,
bypassPermissions) and your observed history (curl | sh, sudo, force-pushes, exfiltration patterns) - MCP — which configured MCP servers are actually used, and which are dead surface
Privacy: reads local files only. Zero network calls. Nothing leaves your machine.
(demo generated from a synthetic corpus — regenerate with python docs/make_demo.py)
Commands
downscope audit
The core command. Scans all local transcripts + settings and prints the coverage matrix, suggestions and risk findings.
downscope audit # terminal report
downscope audit --json # machine-readable, valid JSON on stdout
downscope audit --html report.html # self-contained HTML report (zero external assets)
downscope audit --emit-policy min.json # proposed minimal policy (reviewable fragment)
downscope audit --save-snapshot a.json # snapshot for later drift diffing
downscope audit --project-root . # include this repo's .claude/settings*.json + .mcp.json
The emitted policy is deliberately conservative: used grants are kept, unused grants are demoted to ask (never silently denied), frequent-but-safe patterns are promoted, interpreters (python, node, …) and anything already under ask/deny are never auto-promoted, and a small deny pack covers secrets paths and privilege escalation.
downscope check — CI gate
Fails the build when a repo's committed .claude/settings.json is risky or drifts from a team baseline:
downscope check --project-root . --baseline team-baseline.json --fail-on high
Exit codes: 0 clean, 1 violations, 2 usage error. GitHub Actions snippet:
- name: Check agent permissions
run: |
pip install downscope
downscope check --project-root . --baseline .claude/team-baseline.json
downscope diff — drift over time
downscope audit --save-snapshot before.json
# ... a week of sessions later ...
downscope audit --save-snapshot after.json
downscope diff before.json after.json
Shows rules added/removed, usage drift, new and resolved risk findings, and newly frequent uncovered commands.
How it differs from Claude Code's built-in /fewer-permission-prompts
/fewer-permission-prompts |
downscope |
|
|---|---|---|
| Adds read-only allows from history | yes | no (by design) |
| Finds unused grants | no | yes |
| Risk-scores config + history | no | ~30 deterministic rules |
| Emits minimal policy (shrink) | no | yes, reviewable |
| CI gate against a baseline | no | yes |
| Drift diffing over time | no | yes |
| MCP surface audit | no | yes |
The built-in skill reduces prompt fatigue by adding allows. downscope is the missing other half: the audit layer that shrinks, risk-scores and enforces. Use both.
How matching works (the two hard parts)
- Settings-layer semantics. Rules are matched with Claude Code's documented semantics:
Bashglob forms (git commit *,:*word-boundary sugar), settings-source path anchoring (/src/**vs//abs/pathvs~/…), WebFetch domain rules (*.example.comvs apex), MCP server globs, and layer provenance (user / project / local). - Chain-aware coverage. A chained command (
a && b | c) is only auto-approved when every link is covered, so coverage is decided per segment — with a quote-aware splitter that understands heredocs, comments, redirections and line continuations. Suggestions are generated only from the uncovered links.
Both parts are pinned by a fixture corpus and property-based tests (Hypothesis).
Install (development)
git clone https://github.com/bogdancolceriu/downscope && cd downscope
python -m venv .venv
# Windows: .venv\Scripts\activate POSIX: source .venv/bin/activate
pip install -e ".[dev]"
pytest -q && downscope audit
Tested on Windows, macOS and Linux via CI. Compatibility: transcript formats are undocumented internals; the parser skips unknown records gracefully and the fixture corpus pins the formats seen in Claude Code 2026 releases.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file downscope-0.1.0.tar.gz.
File metadata
- Download URL: downscope-0.1.0.tar.gz
- Upload date:
- Size: 50.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
336334697a2cd484af818843cf62c5ef7748cbff3fa46cf13db1ddfb05119d79
|
|
| MD5 |
a9061f843308028f33badffbdf77e6ba
|
|
| BLAKE2b-256 |
5459156589bec17eb7a921f751c370e5a599d31b4f17bdfb80d64a833d61de1a
|
Provenance
The following attestation bundles were made for downscope-0.1.0.tar.gz:
Publisher:
release.yml on bogdancolceriu/Downscope
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
downscope-0.1.0.tar.gz -
Subject digest:
336334697a2cd484af818843cf62c5ef7748cbff3fa46cf13db1ddfb05119d79 - Sigstore transparency entry: 2349724239
- Sigstore integration time:
-
Permalink:
bogdancolceriu/Downscope@3b973802f4077ff4734bb56e88137000cd51cd99 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/bogdancolceriu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3b973802f4077ff4734bb56e88137000cd51cd99 -
Trigger Event:
push
-
Statement type:
File details
Details for the file downscope-0.1.0-py3-none-any.whl.
File metadata
- Download URL: downscope-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
472ad23c3149bb157e41a15d57d438d04752ca4fe9dab779ae8fd3ef33937718
|
|
| MD5 |
9f0afdeb40bedeaa79f1c80155f0f9b4
|
|
| BLAKE2b-256 |
8afd6c98ba9eaff822076b9e515b6255b75892cc0688880b6a5f03f47665c672
|
Provenance
The following attestation bundles were made for downscope-0.1.0-py3-none-any.whl:
Publisher:
release.yml on bogdancolceriu/Downscope
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
downscope-0.1.0-py3-none-any.whl -
Subject digest:
472ad23c3149bb157e41a15d57d438d04752ca4fe9dab779ae8fd3ef33937718 - Sigstore transparency entry: 2349725070
- Sigstore integration time:
-
Permalink:
bogdancolceriu/Downscope@3b973802f4077ff4734bb56e88137000cd51cd99 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/bogdancolceriu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3b973802f4077ff4734bb56e88137000cd51cd99 -
Trigger Event:
push
-
Statement type: