ASGI protocol inspector — validates, traces, and analyzes your ASGI app
Project description
asgion
ASGI protocol inspector — validates your ASGI application against the ASGI specification at runtime. Catches protocol violations, state machine errors, and event schema mismatches before they become production bugs.
Zero runtime dependencies. Python 3.12+.
Quickstart
Python API
pip install asgion
from asgion import inspect
app = inspect(app) # wrap any ASGI app — zero config
Use with any ASGI server:
import uvicorn
uvicorn.run(inspect(app), host="127.0.0.1", port=8000)
CLI
pip install asgion[cli]
asgion check myapp:app
What It Catches
162 rules across 12 layers — scope fields, event schemas, state machines, extensions, and semantic checks for HTTP, WebSocket, and Lifespan.
[G-005] error Message must be a dict
[HE-017] error response.body 'body' must be bytes, got str
[HF-004] error Duplicate http.response.start
[WE-012] warning websocket.send has both 'bytes' and 'text' set
Every rule has an ID, severity, summary, and hint. See the full list: docs/rules.md
CLI Reference
asgion check
asgion check APP_PATH [OPTIONS]
Check an ASGI app for protocol violations.
| Option | Description |
|---|---|
APP_PATH |
Module:attribute path (e.g. myapp:app) |
--url PATH |
URL paths to check (repeatable, default /) |
--strict |
Exit 1 on any violations |
--format text|json |
Output format (default text) |
--exclude-rules IDS |
Comma-separated rule IDs to skip |
--min-severity LEVEL |
Minimum severity: perf, info, warning, error |
--no-color |
Disable ANSI colors (also respects NO_COLOR env) |
--no-lifespan |
Skip lifespan startup/shutdown checks |
Exit codes: 0 = clean, 1 = violations (with --strict), 2 = runtime error.
asgion rules
asgion rules [OPTIONS]
List all validation rules.
| Option | Description |
|---|---|
--format text|json |
Output format (default text) |
--layer LAYER |
Filter by layer: general, http, websocket, lifespan |
--severity LEVEL |
Filter by severity: perf, info, warning, error |
--no-color |
Disable ANSI colors |
asgion --version
Print version and exit.
Python API
from asgion import inspect
wrapped = inspect(
app,
strict=False, # True to raise on violations
on_violation=lambda v: print(v), # real-time callback
exclude_paths=["/health", "/metrics"], # skip these paths
exclude_rules={"HE-012", "G-008"}, # suppress specific rules
)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
app |
ASGIApp |
required | The ASGI application to wrap |
strict |
bool |
False |
Raise ASGIProtocolError on any violation |
on_violation |
callback | None |
Called with each Violation in real-time |
exclude_paths |
list[str] |
None |
Paths to skip validation |
exclude_rules |
set[str] |
None |
Rule IDs to suppress |
registry |
ValidatorRegistry |
None |
Custom validator registry |
Violation
@dataclass(frozen=True, slots=True)
class Violation:
rule_id: str # "HF-001", "G-010"
severity: Severity # error, warning, info, perf
message: str # human-readable description
hint: str # suggestion for fixing
scope_type: str # "http", "websocket", "lifespan"
path: str # "/api/users"
method: str # "GET"
pytest Plugin
pip install asgion[pytest]
async def test_my_app(asgi_inspect):
app = asgi_inspect(my_app)
async with httpx.AsyncClient(transport=ASGITransport(app)) as client:
resp = await client.get("/users")
assert app.violations == []
Auto-check violations with a marker:
@pytest.mark.asgi_validate(min_severity="error")
async def test_strict(asgi_inspect):
app = asgi_inspect(my_app)
# ... drive the app — violations checked automatically at teardown
Or enable globally for all tests using asgi_inspect:
pytest --asgi-strict
pytest --asgi-strict --asgi-min-severity warning
Comparison
| Feature | asgion | asgiref.testing | Manual testing |
|---|---|---|---|
| Scope validation | 71 rules | basic | none |
| Event schema checks | 42 rules | none | manual |
| State machine (FSM) | 35 rules | none | none |
| Semantic checks | 11 rules | none | none |
| Extension validation | 11 rules | none | none |
| pytest plugin | yes | no | n/a |
| Real-time callbacks | yes | no | n/a |
| CLI tool | yes | no | no |
| Zero dependencies | yes | no (asgiref) | n/a |
| Rule suppression | per-rule | no | n/a |
Contributing
git clone https://github.com/ack1d/asgion.git
cd asgion
uv sync --group dev
uv run pytest # run tests
uv run ruff check src/ # lint
uv run mypy src/ # type check
License
MIT
Project details
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 asgion-0.2.0.tar.gz.
File metadata
- Download URL: asgion-0.2.0.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16925c1a2207b7ee5f06411250ff7c8881a292473b96f0c67d74a2c73e520787
|
|
| MD5 |
077ca051e1c0390e72f51ba0636f9642
|
|
| BLAKE2b-256 |
17293ee62bf0551bac6c321a13a0b91c5156c5b59ca0098e000d3657e9180953
|
Provenance
The following attestation bundles were made for asgion-0.2.0.tar.gz:
Publisher:
publish.yml on ack1d/asgion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
asgion-0.2.0.tar.gz -
Subject digest:
16925c1a2207b7ee5f06411250ff7c8881a292473b96f0c67d74a2c73e520787 - Sigstore transparency entry: 957519198
- Sigstore integration time:
-
Permalink:
ack1d/asgion@bed72739c19cb1481bf9cd5537228285a6847400 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ack1d
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bed72739c19cb1481bf9cd5537228285a6847400 -
Trigger Event:
release
-
Statement type:
File details
Details for the file asgion-0.2.0-py3-none-any.whl.
File metadata
- Download URL: asgion-0.2.0-py3-none-any.whl
- Upload date:
- Size: 41.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dae95b19ad8d68a88bd0388ecb905c07d54bf5ba13819c963bfa9c3d2dda257d
|
|
| MD5 |
e207dcf9a5705fb849714548be60ceb8
|
|
| BLAKE2b-256 |
306338fe7e2c1b5a19037b4ce7ab54f0f0c9e5eaddadf0301d140a725a32fbf3
|
Provenance
The following attestation bundles were made for asgion-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on ack1d/asgion
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
asgion-0.2.0-py3-none-any.whl -
Subject digest:
dae95b19ad8d68a88bd0388ecb905c07d54bf5ba13819c963bfa9c3d2dda257d - Sigstore transparency entry: 957519205
- Sigstore integration time:
-
Permalink:
ack1d/asgion@bed72739c19cb1481bf9cd5537228285a6847400 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ack1d
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@bed72739c19cb1481bf9cd5537228285a6847400 -
Trigger Event:
release
-
Statement type: