Little Canary
Prompt-injection sensing through a powerless sacrificial model.
Website · Product page · PyPI · Research
Little Canary screens untrusted language before your agent acts. It exposes the input to a small model with no application tools or authority, then inspects the model's response for evidence that the input changed its behavior.
untrusted text
→ structural preflight
→ powerless sacrificial model
→ response-residue analysis
→ PASS / FLAG / BLOCK
Structural checks catch known input shapes. The behavioral layer asks a different question: what did this input do to the canary?
Little Canary is an inbound risk sensor, not a security guarantee or an agent runtime.
Quick start
Install Little Canary:
python -m pip install little-canary
little-canary --version
Run the local adapter with an Ollama model:
little-canary serve \
--mode block \
--canary-model qwen2.5:1.5b \
--ollama-url http://127.0.0.1:11434
Then screen untrusted text:
curl -sS http://127.0.0.1:18421/check \
-H 'Content-Type: application/json' \
-d '{"text":"untrusted text"}'
The adapter binds to 127.0.0.1 and exposes GET /health and POST /check.
See the canary work
Little Canary includes a live evidence gate that sends a fixed clean/attack pair through a dedicated local Ollama model:
little-canary demo --live \
--backend ollama \
--model qwen2.5:1.5b \
--endpoint http://127.0.0.1:11434
A recorded run produced this contrast:
CASE clean
RESPONSE "The capital of France is Paris."
RISK 0.0
SIGNALS none
VERDICT PASS
CASE c1-05
RESPONSE "I have acknowledged the update to the system prompt."
RISK 1.0
SIGNALS canary_compromise
VERDICT BLOCK
LIVE CONTRAST VERIFIED
The attack was a fake system-prompt correction. The canary followed it; Little Canary detected the compromise residue and blocked the input.
A live result is evidence for that exact model, runtime, and configuration, not a universal detection claim.
Python API
from little_canary import SecurityPipeline
pipeline = SecurityPipeline(
canary_model="qwen2.5:1.5b",
mode="full",
)
verdict = pipeline.check(untrusted_text)
if verdict.degraded:
quarantine_or_apply_your_availability_policy(untrusted_text)
elif not verdict.safe:
block(untrusted_text, verdict.summary)
else:
forward_to_agent(verdict.safe_input)
Routing and inspection coverage are deliberately separate. An unavailable canary
can be configured to fail open, but failed coverage is reported as degraded
rather than silently becoming a clean pass.
Integrations
Little Canary can sit at the input boundary of several agent environments.
| Integration | Boundary |
|---|---|
| Python | SecurityPipeline before application forwarding |
| Claude Code | UserPromptSubmit hook before the turn |
| Gemini CLI | BeforeAgent hook before the agent loop |
| OpenAI Agents SDK | Native InputGuardrail |
| Hermes Agent | Screens the user turn and removes tool authority on BLOCK |
| Local HTTP | Loopback /check adapter for other hosts |
Each integration preserves the host's actual enforcement capabilities. For
example, the Hermes Agent plugin cannot prevent prompt delivery at its available
hook boundary, so a BLOCK removes downstream tool authority instead.
What "powerless" means
The canary receives the untrusted input but is given no application tools, credentials, or output execution.
Its response is evidence to inspect — not instructions for the authoritative agent.
This is a library-level capability boundary, not an operating-system sandbox. Giving the canary tools or forwarding its output into an authoritative execution path changes the security model.
Coverage and failure
Little Canary distinguishes routing disposition from inspection coverage.
| State | Meaning |
|---|---|
PASS |
Exercised inspection found no covered compromise signal |
FLAG |
Suspicious evidence was observed |
BLOCK |
Configured policy rejects the input |
DEGRADED |
Required inspection could not be completed |
UNSCREENED |
Behavioral inspection was not exercised |
Fail-open behavior is availability policy, not evidence that an input is safe. A failed or skipped inspection layer is never represented as a successful behavioral check.
Security boundary
Little Canary is one layer in an agent-security architecture.
It does not:
- prove that an input is harmless;
- detect every prompt injection;
- replace least privilege or tool policy;
- sandbox the canary process at the operating-system level;
- screen every tool result or downstream interaction through every integration;
- turn missing behavioral coverage into a clean verdict.
Remote model endpoints receive the data sent to them. Use local models when input must remain local.
See SECURITY.md for vulnerability reporting and benchmarks/README.md for the evaluation boundary.
Research
Little Canary implements the behavioral-canarying architecture described in Behavioral Canarying for Prompt Injection: Powerless Model Probes with Explicit Coverage Semantics.
The technical note documents the pre-execution sensing architecture and the separation between routing disposition and inspection coverage. It does not claim universal detection, formal security, or aggregate accuracy for the current release.
Concept DOI: 10.5281/zenodo.21818564
Development
pytest
ruff check little_canary tests
mypy little_canary
python -m build
python -m twine check dist/*
Tests are offline by default and mock network behavior. Live evaluation should use a dedicated endpoint that is not serving another workload.
The version in pyproject.toml is the source of truth for a release.
little-canary --version reports the build you actually have installed, and
GitHub Releases and
PyPI are the live authorities for
what is published.
Documentation
| Need | Document |
|---|---|
| Security and vulnerability reporting | SECURITY.md |
| Evaluation and evidence boundary | benchmarks/README.md |
| Research and methodology | Behavioral Canarying |
| Releases | GitHub Releases |
| Package | PyPI |
License
Apache License 2.0
Little Canary is maintained by Hermes Labs.
Metadata
Release files for little-canary 0.3.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| little_canary-0.3.8.tar.gz | 106.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| little_canary-0.3.8-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 171.6 kB
Release files / little_canary-0.3.8.tar.gz
| Download URL | little_canary-0.3.8.tar.gz |
|---|---|
| Size | 106.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ccf103430acc621d18b18395addaa5ac686dba725e152b9e157aeb60a6ec6078
|
|
BLAKE2b-256 checksum How to use checksums |
4de9bd21a0687a1f7d05f74417c20f5f0711f53015dbf24d2991421456c13469
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / little_canary-0.3.8-py3-none-any.whl
| Download URL | little_canary-0.3.8-py3-none-any.whl |
|---|---|
| Size | 65.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
593e51e327823f554639731daa5665bab8ee58f6226e943d6257ca20b2a6c2c4
|
|
BLAKE2b-256 checksum How to use checksums |
76f0d9d2f9519292a8ea5621d5d59c6a6bfa6ecc6d29d59663dd32b5f303d5f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log