ASGI protocol inspector — validates, traces, and analyzes your ASGI app
Project description
asgion
ASGI protocol inspector and trace engine. Catch subtle protocol violations your tests miss — before they hit production.
Why asgion?
ASGI apps can pass all tests while still violating the protocol:
- Sending response body before
http.response.start - Writing to a closed WebSocket connection
- Exiting without completing a streaming response
- Returning malformed event payloads
Frameworks catch some of this. asgion validates the full ASGI contract — state machines, event schemas, scope fields, and semantic constraints across HTTP, WebSocket, and Lifespan.
Highlights
- Full ASGI contract validation — 164 rules across HTTP, WebSocket, and Lifespan
- Trace engine — record every
receive()/send()with nanosecond timestamps and inline violation markers - CI-ready — deterministic exit codes and JSON output
- CLI, Python API, pytest plugin — fits any workflow
- Zero runtime dependencies — pure Python 3.12+
- O(1) per message — safe for hot paths, no overhead when tracing is off
Works with any ASGI app: FastAPI, Starlette, Litestar, Django (ASGI), or bare ASGI handlers.
Quickstart
Get started in under a minute:
pip install asgion[cli]
Given a buggy app that sends body before http.response.start:
# myapp.py
async def app(scope, receive, send):
if scope["type"] == "http":
await send({"type": "http.response.body", "body": b"oops"}) # wrong order
await send({"type": "http.response.start", "status": 200, "headers": []})
await send({"type": "http.response.body", "body": b"hello"})
Check finds protocol violations:
asgion check myapp:app
CHECK myapp:app
── GET / ─────────────────────────────────────────────────────────
[HF-002] error: http.response.body sent without preceding http.response.start
hint: Send http.response.start before any http.response.body
[SEM-002] info: No Content-Type header on 2xx response
hint: Responses with a body should include a Content-Type header
2 violations (1 error, 1 info)
What is HF-002? Look up any rule directly:
asgion rules HF-002
RULE [HF-002] error
http.response.body sent without preceding http.response.start
hint: Send http.response.start before any http.response.body
layer: http.fsm
applies to: http
Trace shows the full event timeline for the same app:
asgion trace myapp:app
TRACE GET / (0.063ms, TTFB 0.035ms)
0.016ms send http.response.body 4 bytes ← HF-002 (error)
0.035ms send http.response.start 200 (+0.019ms) ← SEM-002 (info)
0.052ms send http.response.body 5 bytes (+0.016ms)
Events: 3 | Violations: 2 (1 error, 1 info)
asgion trace myapp:app --min-severity error # only error-level markers
asgion trace myapp:app --out ./traces/ # save as JSON files
Exit codes: 0 = clean, 1 = violations (--strict), 2 = runtime error. See asgion check --help.
See the full rule list for all available rules and their descriptions.
Python API
from asgion import Inspector
inspector = Inspector(app)
# ... drive the app via httpx, TestClient, etc. ...
assert inspector.violations == [] # fails if any protocol violation occurred
With tracing:
inspector = Inspector(app, trace=True)
async with httpx.AsyncClient(transport=ASGITransport(inspector)) as client:
await client.get("/api/users")
record = inspector.traces[0]
record.scope.method # "GET"
record.scope.path # "/api/users"
record.summary.ttfb_ns # time to first byte (ns)
As middleware:
from asgion import inspect
uvicorn.run(inspect(app))
Pytest Plugin
Integrate protocol validation directly into your test suite:
pip install asgion[pytest]
async def test_my_app(asgi_inspect):
inspector = asgi_inspect(my_app)
async with httpx.AsyncClient(transport=ASGITransport(inspector)) as client:
await client.get("/users")
assert inspector.violations == []
pytest --asgi-strict # auto-check all tests
Configuration
Via pyproject.toml, .asgion.toml, or Python API.
See configuration docs and full rule list.
[tool.asgion]
profile = "recommended"
exclude_rules = ["SEM-006"]
Contributing
git clone https://github.com/ack1d/asgion.git
cd asgion
uv sync --group dev
uv run pytest
If you have Task installed: task check runs lint, typecheck, and tests.
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.5.1.tar.gz.
File metadata
- Download URL: asgion-0.5.1.tar.gz
- Upload date:
- Size: 43.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7700f73087619546c62ae00b447c7a3de636de6b525b28ca95acdefb23e39ae6
|
|
| MD5 |
e9b130332f20f5e05a586a3b4f15c45a
|
|
| BLAKE2b-256 |
8313476a43ae08302d205945b9c456a686423446e98d45c763f8f93d8028a564
|
Provenance
The following attestation bundles were made for asgion-0.5.1.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.5.1.tar.gz -
Subject digest:
7700f73087619546c62ae00b447c7a3de636de6b525b28ca95acdefb23e39ae6 - Sigstore transparency entry: 999596492
- Sigstore integration time:
-
Permalink:
ack1d/asgion@cdc6fc0555cf88b28246a34fcab0f03b9e0f746e -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/ack1d
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cdc6fc0555cf88b28246a34fcab0f03b9e0f746e -
Trigger Event:
release
-
Statement type:
File details
Details for the file asgion-0.5.1-py3-none-any.whl.
File metadata
- Download URL: asgion-0.5.1-py3-none-any.whl
- Upload date:
- Size: 60.4 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 |
116c6b3948e02f6ccbfc0a8dc4925e2026fe7a34fbe9c31ad7a2e93918dcfbef
|
|
| MD5 |
59ceeab6f51fae7d22da51d7eebce24e
|
|
| BLAKE2b-256 |
d97e4a5f6373bbb99b93d9e3b756fd301f960ca558b819c541bb8f62fc89e009
|
Provenance
The following attestation bundles were made for asgion-0.5.1-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.5.1-py3-none-any.whl -
Subject digest:
116c6b3948e02f6ccbfc0a8dc4925e2026fe7a34fbe9c31ad7a2e93918dcfbef - Sigstore transparency entry: 999596544
- Sigstore integration time:
-
Permalink:
ack1d/asgion@cdc6fc0555cf88b28246a34fcab0f03b9e0f746e -
Branch / Tag:
refs/tags/v0.5.1 - Owner: https://github.com/ack1d
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cdc6fc0555cf88b28246a34fcab0f03b9e0f746e -
Trigger Event:
release
-
Statement type: