Canari
Agent component verification ledger - signed, offline-verifiable, history-preserving attestations for MCP servers, agent skills, and DSH plugins.
Named after the canary in the coal mine: the first to detect that something went wrong.
What it does
- Weekly scan of agent component ecosystems (npm, PyPI, MCP Registry, dsh.so)
- Each component gets a signed attestation (DSSE/in-toto, ed25519) binding: artifact hash + host version + scan scenario
- Attestations are offline-verifiable by anyone with the public key
- History is never deleted - version changes create supersession chains
- Public machine-readable feed for any AI agent or host to consume
- Trust heuristics: release cadence (L2.4), metadata anomaly (L2.5), typosquatting (L2.6)
- Consumer install gate with policies:
canari-verify bundle.json --key pub.json --gate --gate-policy paranoid
Install
From the latest GitHub release (wheel + sdist, checksum + Ed25519 signed):
pip install https://github.com/lanbass869-cell/canari/releases/download/v0.1.0/canari_mcp-0.1.0-py3-none-any.whl
canari-gate --store out --list
Then verify what you downloaded (see docs/release-process.md for the one-liner).
Quality gates (enforced in CI)
| Gate | Threshold | Current |
|---|---|---|
| tests | green on 3.10/3.12/3.13 | 357 passing |
| line coverage | >= 85% | 90% |
| mutation kill rate | >= 90% (equivalent mutants excluded) | 100% (50/50) |
| live-data audit | sampled attestations re-derive from npm/PyPI | 18/18 |
Run them yourself: python scripts/quality_gate.py
Quick start
# install
pip install canari-mcp
# run a scan
canari-baseline --limit 50
# verify an attestation offline
canari-verify out/attestations/npm_some-package.bundle.json --key out/public_key.json
# verify + policy gate (exit 0 ALLOW / 5 BLOCK / 6 REVIEW)
canari-verify out/attestations/npm_some-package.bundle.json --key out/public_key.json \\
--gate --gate-policy strict
# custom policy file
canari-verify bundle.json --key out/public_key.json --gate --gate-policy-file my-policy.json
Policy file example
{"name": "my-policy", "block_on_findings": true, "require_native_provenance": false}
Consumer side (the part a host actually uses)
1. Look up a component and gate it
# resolve the newest signed attestation for a component and run the gate
canari-gate --store out --registry npm --name express
canari-gate --store out --registry dsh-registry --name aegis --policy paranoid --json
# exit codes: 0 ALLOW, 5 BLOCK, 6 REVIEW, 3 not found
canari-gate --store out --list
2. Drift report (what changed since last week)
canari-drift --store out --write out/drift
# verdict flips / same-version silent changes / findings added / findings removed
run_weekly.py writes this report automatically after every scan.
3. Use it from any MCP host (dsh, Codex, Claude, ...)
canari-mcp speaks MCP over stdio (zero dependencies) and exposes three tools:
gate_check, verify_bundle, store_summary.
{
"mcpServers": {
"canari": {
"command": "canari-mcp",
"args": ["--store", "E:/Temp/tbdig/canari/out",
"--key", "E:/Temp/tbdig/canari/out/public_key.json"]
}
}
}
A host (or agent) can then ask: "is npm:express allowed to install?" and get
ALLOW / BLOCK / REVIEW with reasons, backed by a signed, offline-verifiable
attestation.
4. Gate the actual install command
canari-guard wraps an install command, checks every component against the
ledger, and refuses to execute when the verdict blocks:
canari-guard -- dsh plugin --profile wbpj add dsh-purge
canari-guard -- npm install some-mcp-server
canari-guard -- pip install mcp-server-fetch==0.2.1
# exit 0 allowed+executed | 5 blocked by ledger | 6 review not confirmed
# --dry-run prints what would run; --allow-review accepts pass-with-findings
The host is never patched - the guard sits in front of the install command.
For DeepSeek Harness (dsh), add this to the profile's cordis.patch.yml
(verified end-to-end on dsh: the model called mcp__canari__store_summary and
received the live component count):
- id: mcp-canari
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: canari
transport: stdio
command: 'E:\Temp\tbdig\_venv_pr_a\Scripts\python.exe'
args: ['-m', 'canari.mcp_server', '--store', 'E:\Temp\tbdig\canari\out',
'--key', 'E:\Temp\tbdig\canari\out\public_key.json']
cwd: 'E:\Temp\tbdig\canari'
failOnStartupError: true
Multi-registry coverage
| Registry | Source | What we check | What we inherit |
|---|---|---|---|
| npm | search API + packument + tarball sha256 | existence, version, artifact hash, maintainers | - |
| PyPI | Simple API + JSON API | same | - |
| MCP Registry | official REST | identity presence | - |
| dsh.so | JSON API | reference existing L1-L5 conclusions (not re-scanned) | dsh.so verdicts |
Honest limitations
- This is a risk signal, not a safety guarantee
- M0/M1 covers meta-level checks (L1-L2); behavioral checks (L3+) are M1+
- Scan coverage is limited to what registries expose publicly
expires_atis 7 days - attestations go stale, re-scan needed
The threat model this addresses
postmark-mcp (MITRE ATLAS AML.CS0053): 15 clean versions, then one poisoned commit, ~300 orgs affected. After takedown, no host was notified. The gap: no longitudinal observation, no cross-host revocation feed, no portable verdict.
Canari fills that gap.
License
Apache-2.0. See LICENSE and SECURITY.md.
Metadata
Release files for canari-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| canari_mcp-0.1.0.tar.gz | 105.8 kB | Without provenance | Details for canari_mcp-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| canari_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for canari_mcp-0.1.0-py3-none-any.whl |
Total release size: 177.6 kB
Release files / canari_mcp-0.1.0.tar.gz
| Download URL | canari_mcp-0.1.0.tar.gz |
|---|---|
| Size | 105.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
68a067abb79ad3d895d846e42d1195c381e1b406e7a1d68b6417f2e1bf86d1ce
|
|
BLAKE2b-256 checksum How to use checksums |
8fae4285c19423237a5b69fbbf06ef18384a6047303d7e17664621dd1e8508e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / canari_mcp-0.1.0-py3-none-any.whl
| Download URL | canari_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 71.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
41ab9afd4c03becba70b626cc66702d36428139d94331b2f8caa66c376fd5ff2
|
|
BLAKE2b-256 checksum How to use checksums |
51e646e99f1ab7dfc6cab03dd4a9fd2f79b66865b32157f812ea6d70fe8d87a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for canari-mcp 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.