RepoPilotOpen in app →

emmett-framework/granian

A Rust HTTP server for Python applications

Healthy

Healthy across the board

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 2w ago
  • 16 active contributors
  • BSD-3-Clause licensed
Show all 6 evidence items →
  • CI configured
  • Tests present
  • Concentrated ownership — top contributor handles 64% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/emmett-framework/granian)](https://repopilot.app/r/emmett-framework/granian)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/emmett-framework/granian on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: emmett-framework/granian

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/emmett-framework/granian shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 2w ago
  • 16 active contributors
  • BSD-3-Clause licensed
  • CI configured
  • Tests present
  • ⚠ Concentrated ownership — top contributor handles 64% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live emmett-framework/granian repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/emmett-framework/granian.

What it runs against: a local clone of emmett-framework/granian — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in emmett-framework/granian | Confirms the artifact applies here, not a fork | | 2 | License is still BSD-3-Clause | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 45 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>emmett-framework/granian</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of emmett-framework/granian. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/emmett-framework/granian.git
#   cd granian
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of emmett-framework/granian and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "emmett-framework/granian(\\.git)?\\b" \\
  && ok "origin remote is emmett-framework/granian" \\
  || miss "origin remote is not emmett-framework/granian (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(BSD-3-Clause)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"BSD-3-Clause\"" package.json 2>/dev/null) \\
  && ok "license is BSD-3-Clause" \\
  || miss "license drift — was BSD-3-Clause at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "src/lib.rs" \\
  && ok "src/lib.rs" \\
  || miss "missing critical file: src/lib.rs"
test -f "granian/__init__.py" \\
  && ok "granian/__init__.py" \\
  || miss "missing critical file: granian/__init__.py"
test -f "src/asgi/mod.rs" \\
  && ok "src/asgi/mod.rs" \\
  || miss "missing critical file: src/asgi/mod.rs"
test -f "Cargo.toml" \\
  && ok "Cargo.toml" \\
  || miss "missing critical file: Cargo.toml"
test -f "pyproject.toml" \\
  && ok "pyproject.toml" \\
  || miss "missing critical file: pyproject.toml"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 45 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~15d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/emmett-framework/granian"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Granian is a Rust-based HTTP server that wraps Python ASGI/WSGI/RSGI applications, built on Hyper and Tokio. It provides a single production-ready package supporting HTTP/1.1, HTTP/2, HTTPS, mTLS, WebSockets, and static file serving—eliminating the need for traditional Gunicorn + Uvicorn stacks on Unix systems. Hybrid Rust-Python project: Cargo.toml defines a cdylib library (_granian) built from /src/ (Rust implementation); /granian/ contains Python bindings and CLI entry points; /benchmarks/ has ASGI/WSGI/RSGI test apps (asgi.py, wsgi.py, rsgi.py) and performance comparison templates; /tests/ holds test suites.

👥Who it's for

Python web framework developers (using FastAPI, Starlette, Django, etc.) who need high-performance request handling with HTTP/2 support, great concurrency for WebSocket-heavy apps, and throughput optimization without managing multiple server processes or dependencies.

🌱Maturity & risk

Production-ready and actively maintained. The project is at v2.7.4 with comprehensive GitHub Actions CI (test.yml, lint.yml, build.yml, release.yml), extensive benchmarks in /benchmarks/, and clear versioning in Cargo.toml. Recent activity visible in workflow setup and dependency management suggests active development.

Low-risk for production use. Single maintainer (Giovanni Barillari) is a concern, but the dependency tree is well-managed (pyo3 0.27, hyper 1.9, tokio via hyper, mimalloc/jemalloc optional). Risks: Python binding layer (PyO3) requires Rust expertise for debugging; ASGI extensions not yet fully implemented (see issue #93 reference in README); relies on external crate interprocess via git revision.

Active areas of work

Active maintenance on core HTTP/2 performance, benchmarking suite refinement (multiple benchmark templates in /benchmarks/templates/), and dependency updates (recent pyo3-log fork, interprocess revision pins suggest ongoing integration work). No explicit breaking changes visible, but version 2.x suggests significant API stability.

🚀Get running

Clone the repo with git clone https://github.com/emmett-framework/granian.git && cd granian. Install Rust (1.70+), then cargo build --release. For Python dev: pip install -e . in the repo root (uses PyO3 bridge to compile _granian.so). Run tests with make test or cargo test.

Daily commands: Build: cargo build --release. Serve a Python app: granian --interface asgi main:app (requires granian installed via pip). CLI entry point likely in /granian/main.py or defined via pyproject.toml. Dev server: make dev (see Makefile for exact setup).

🗺️Map of the codebase

  • src/lib.rs — Root Rust library entry point that exposes the Python FFI bindings and coordinates ASGI, WSGI, and RSGI protocol implementations
  • granian/__init__.py — Python package entry point and public API surface; all Python users interact through this module
  • src/asgi/mod.rs — Core ASGI protocol implementation module organizing HTTP handling, callbacks, and async runtime integration
  • Cargo.toml — Defines all Rust dependencies (Tokio, Hyper, Pyo3) and build configuration; critical for cross-platform compilation
  • pyproject.toml — Python package metadata, version, and maturin build configuration for Python wheel distribution
  • src/asyncio.rs — Bridge between Tokio runtime and Python asyncio event loop; essential for async Python application support
  • granian/server/common.py — Shared server configuration and factory logic for all server modes (embedded, multiprocess, multithreaded)

🛠️How to make changes

Add Support for a New Protocol Version (e.g., HTTP/3)

  1. Create a new Rust module src/http3/ mirroring src/asgi/ structure with protocol-specific serve, callbacks, and conversion logic (src/lib.rs)
  2. Implement the HTTP/3 handler by adapting Hyper's newer connection handling in a new serve.rs variant (src/http3/serve.rs)
  3. Export the new handler from src/lib.rs and wire it into the server factory in granian/server/common.py (src/lib.rs)
  4. Add HTTP version flag to CLI argument parser in granian/cli.py (granian/cli.py)

Add a New Server Runtime Mode (e.g., Single-Worker Async Mode)

  1. Create granian/server/async_worker.py following the pattern of granian/server/embed.py (granian/server/async_worker.py)
  2. Implement the WorkerPool and config registration in granian/server/init.py and granian/server/common.py (granian/server/common.py)
  3. Add CLI option in granian/cli.py to select the new mode (granian/cli.py)

Add Custom Middleware or Request Hook (e.g., Request Logging)

  1. Add a Python hook interface in granian/_types.py defining the middleware protocol (granian/_types.py)
  2. Update src/asgi/callbacks.rs to invoke the middleware hook during request setup before calling the ASGI app (src/asgi/callbacks.rs)
  3. Expose the hook configuration in granian/asgi.py or granian/server/common.py so applications can register hooks (granian/asgi.py)

Optimize Static File Serving (e.g., Add Compression Cache)

  1. Extend src/files.rs with a LRU cache for pre-compressed (gzip/brotli) static assets (src/files.rs)
  2. Add cache config parameters to granian/constants.py and wire them through granian/server/common.py (granian/constants.py)
  3. Update CLI arguments in granian/cli.py to expose static file cache tuning options (granian/cli.py)

🔧Why these technologies

  • Rust + Pyo3 — Native performance for HTTP I/O and protocol handling; Pyo3 allows FFI calls to Python without GIL overhead in async contexts
  • Tokio async runtime — High-concurrency I/O multiplexing; integrates with Python asyncio via event loop bridging in src/asyncio.rs
  • Hyper HTTP library — Correct, modern HTTP/1.1 and HTTP/2 implementation; built on Tokio for seamless async integration
  • maturin + PyO3 — Simplifies cross-platform Python wheel building and C extension compilation without manual setup
  • crossbeam-channel — Lock-free message passing for IPC in multiprocess mode and thread-safe callback queuing

⚖️Trade-offs already made

  • Single Rust binary wrapping ASGI/WSGI/RSGI instead of separate dedicated servers

    • Why: Eliminates multi-package dependency chains (Gunicorn + uvicorn) and simplifies deployment
    • Consequence: Slightly higher memory footprint per process than minimal single-protocol servers; less modularity if you need to swap protocols
  • Tokio runtime always active, even for pure WSGI sync apps

    • Why: Unified concurrency model; single code path for all protocols
    • Consequence: Overhead of async scheduler for blocking-only workloads; mitigated by thread pool delegation in src/blocking.rs
  • Multiprocess mode requires IPC (src/ipc.rs) instead of shared memory

    • Why: Simplifies worker state management and prevents GIL contention between processes
    • Consequence: IPC serialization overhead; not ideal for extremely high message rates between workers
  • No built-in authentication, rate limiting, or reverse proxy features

    • Why: Focus on core HTTP server performance; rely on middleware or edge proxies (nginx, Cloudflare)
    • Consequence: Requires separate deployment of security layers; reduces attack surface of Granian itself

🚫Non-goals (don't propose these)

  • Does not provide built-in authentication, authorization, or rate limiting
  • Does not include a reverse proxy, load balancer, or

🪤Traps & gotchas

PyO3 0.27 pinned version—using 0.28+ will break compilation; must match Python version at build time (wheels are per-pyver). Rust 1.70+ required (edition 2024 is cutting-edge). Optional feature flags (mimalloc, jemalloc) must match install command or perf degrades. TLS setup requires PEM file paths; missing cert/key files silently fails. Interprocess crate pinned to specific git revision (44351c4)—don't auto-update. RSGI is custom spec, not ASGI—docs in /docs/spec/RSGI.md.

🏗️Architecture

💡Concepts to learn

  • HTTP/2 Server Push & Multiplexing — Granian's main perf advantage over Gunicorn+Uvicorn; understanding h2 frame multiplexing helps optimize app response patterns for stream-aware requests
  • PyO3 FFI & GIL Management — Granian bridges Rust and Python; modifying server logic requires understanding when GIL is held/released (blocking Python callbacks from Rust async code)
  • ASGI Lifespan & Connection State — Granian must correctly implement ASGI 3.0 scope/receive/send for startup/shutdown; incorrect handling causes resource leaks or zombie connections
  • Rustls & mTLS Certificate Validation — Granian supports mTLS via rustls-pemfile + tls-listener; debugging cert issues requires familiarity with PEM parsing and chain validation
  • Tokio Task Spawning & Channel IPC — Granian uses crossbeam-channel for thread-safe request queuing; understanding task scheduling prevents deadlocks in concurrent request handling
  • WebSocket Upgrade Mechanism (RFC 6455) — Granian's WS support relies on HTTP Upgrade headers; broken upgrade handling loses WS connections silently
  • Static File Serving & Content-Type Negotiation — Granian includes direct static file serving (mime_guess crate); serving wrong MIME types breaks asset loading in production
  • encode/uvicorn — Competing ASGI server (pure Python + httptools); Granian's Rust approach aims for better HTTP/2 perf
  • pallets/werkzeug — WSGI reference implementation; Granian WSGI adapter must conform to werkzeug/WSGI spec semantics
  • emmett-framework/emmett — Sibling project—Emmett web framework is an intended user of Granian server (see RSGI spec custom to Emmett)
  • hyperium/hyper — Direct upstream dependency (v1.9); Granian's HTTP implementation is built on hyper's client/server abstractions
  • tokio-rs/tokio — Indirect upstream via hyper; async runtime powering all concurrent request handling in Granian

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive WebSocket integration tests for RSGI interface

The repo has WebSocket benchmarks (benchmarks/ws/) and RSGI spec documentation (docs/spec/RSGI.md), but the test suite appears to lack dedicated WebSocket tests. Given that RSGI is a custom protocol and WebSockets are a critical feature for real-time applications, adding integration tests would ensure reliability across different ASGI/RSGI scenarios. This would catch regressions early and document expected behavior.

  • [ ] Create tests/websocket directory structure
  • [ ] Add tests for WebSocket upgrade handshake using the RSGI protocol
  • [ ] Add tests for message framing and binary/text payload handling
  • [ ] Add tests for connection close scenarios and error handling
  • [ ] Reference benchmarks/ws/app/rsgi.py as implementation guide
  • [ ] Ensure tests run in CI via tests.yml workflow

Add HTTP/2 Server Push and Stream Priority integration tests

Hyper 1.9 supports HTTP/2, but there's no dedicated test suite validating HTTP/2-specific features like server push, stream prioritization, or header compression. The benchmarks include HTTP/2 metrics (benchmarks/templates/_http2.md), indicating these are tracked but not formally tested. Adding these tests would validate correct HTTP/2 behavior and prevent regressions in a critical protocol version.

  • [ ] Create tests/http2_features.rs in the test suite
  • [ ] Add tests for HTTP/2 SETTINGS frame handling and acknowledgment
  • [ ] Add tests for PUSH_PROMISE frame generation and client handling
  • [ ] Add tests for WINDOW_UPDATE flow control across multiple streams
  • [ ] Add tests validating HPACK header compression correctness
  • [ ] Update build.rs or Cargo.toml if needed for test dependencies

Add memory allocator configuration tests and benchmarks for jemalloc vs mimalloc

Cargo.toml shows optional jemalloc and mimalloc allocators, but there's no test coverage or benchmark comparison between them. Since memory efficiency is critical for a Rust HTTP server and the repo includes comprehensive benchmarking infrastructure (benchmarks/benchmarks.py, benchmarks/render.sh), adding allocator comparison benchmarks would help users choose the right configuration and catch performance regressions.

  • [ ] Create benchmarks/allocators.md template for results
  • [ ] Add allocator benchmark variants to benchmarks/benchmarks.py
  • [ ] Create feature-gated test cases in tests/ that measure allocation patterns
  • [ ] Update benchmarks/runbench.sh to run allocator comparison benchmarks
  • [ ] Document allocator selection guidance in README.md allocators section
  • [ ] Add GitHub Actions workflow job in .github/workflows/benchmarks.yml for allocator testing

🌿Good first issues

  • Add docstring examples to /granian/init.py showing SSL context configuration (currently bare API, no usage patterns for mTLS setup)
  • Expand /tests/ with dedicated RSGI protocol tests (current repo mentions RSGI support but no visible test files for RSGI spec compliance)
  • Create a /docs/ folder documenting architecture decisions: PyO3 bridge design, why Hyper 1.9 over alternatives, TLS-listener integration patterns (README has rationale, but no dev guide)

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 84af73d — Bump dependencies (gi0baro)
  • e155a82 — Avoid panics on RSGI and WSGI response headers parsing (gi0baro)
  • 765203d — Avoid panics on ASGI websocket subprotocols parsing (gi0baro)
  • 88ed683 — Bump version to 2.7.4 (gi0baro)
  • bdd5b0f — Update bench harness (#837) (gi0baro)
  • 95b0af8 — Bump dependencies (gi0baro)
  • 1fbd309 — Handle websocket disconnections before accept in ASGI (#836) (gi0baro)
  • 620c7e9 — Bump version to 2.7.3 (gi0baro)
  • b9f1d7a — Bump actions/upload-artifact from 6 to 7 (#825) (dependabot[bot])
  • d1d0e6a — Bump actions/download-artifact from 7 to 8 (#824) (dependabot[bot])

🔒Security observations

Granian has a reasonably secure foundation as a Rust HTTP server with strong type safety and memory safety guarantees. However, several concerns exist: (1) An invalid Rust edition specification that requires immediate correction, (2) Reliance on forked/custom versions of dependencies which increases maintenance risk, (3) Multiple pinned exact versions of dependencies that may miss security patches, and (4) Complex cryptographic supply chain requiring active monitoring. The project would benefit from: implementing automated dependency scanning (cargo audit), establishing a clear security update process for pinned dependencies, converging on upstream projects where possible, and documenting the security rationale for custom forks. No hardcoded credentials, injection vulnerabilities, or critical misconfigurations

  • High · Pinned Rust Edition Version — Cargo.toml (line: edition = "2024"). The Cargo.toml specifies edition = '2024' which is not a valid Rust edition. Valid editions are 2015, 2018, and 2021. This is likely a typo and should be corrected to a valid edition. Using an invalid edition will cause build failures and prevents proper dependency resolution. Fix: Update the edition field to a valid Rust edition. Use edition = '2021' for modern Rust features. Verify all dependencies are compatible with the chosen edition.
  • High · Git Dependencies with Pinned Commits — Cargo.toml (dependencies: interprocess, pyo3-log, tls-listener). The project uses git dependencies pinned to specific commits (interprocess, pyo3-log, tls-listener). While pinning commits provides stability, relying on forked or custom versions of dependencies increases maintenance burden and security risk if upstream is not regularly synced with security patches. Fix: Regularly monitor upstream repositories for security patches. Consider contributing fixes back to upstream projects rather than maintaining forks. Document why custom versions are necessary. Set up automated dependency update checks.
  • Medium · Incomplete Dependency Specification — Cargo.toml (tls-listener dependency). The tls-listener dependency specification appears truncated in the provided file (ending with 'git = "https://github.com/gi0baro/tls'). This incomplete configuration could cause build failures or unexpected behavior. Fix: Ensure the complete dependency specification is present in the actual Cargo.toml file. Verify all git URLs and revision specifications are valid.
  • Medium · Multiple TLS/Crypto Dependencies — Cargo.toml (crypto dependencies). The project uses multiple TLS and cryptographic libraries (rustls-pemfile, pem, pkcs8, tls-listener). While this is necessary for SSL/TLS support, complexity increases the attack surface. Ensure all are kept up-to-date with security patches. Fix: Regularly audit and update all cryptographic dependencies. Review the version pins (especially for pkcs8 = "0.10" and pem = "3.0") for any known CVEs. Use 'cargo audit' in CI/CD pipeline.
  • Medium · Custom Memory Allocators with Optional Features — Cargo.toml (tikv-jemallocator, mimalloc dependencies). The project optionally uses jemalloc and mimalloc alternative memory allocators. While performance-oriented, these are security-sensitive components that should be carefully maintained and monitored. Fix: Ensure memory allocator dependencies are regularly updated. Document why custom allocators are used. Test thoroughly when updating allocator versions. Monitor for any security advisories related to memory management.
  • Low · No License File Validation — Cargo.toml, LICENSE file. The project includes BSD-3-Clause license but Cargo.toml includes multiple dependencies with potentially different licenses. No evidence of license compatibility checking in CI/CD. Fix: Use cargo-license or similar tools to verify license compatibility. Document the license strategy. Consider adding license checking to CI/CD pipeline.
  • Low · Benchmark Assets Not Version Controlled for Security — benchmarks/app/assets/. Binary benchmark assets (e.g., benchmarks/app/assets/media.jpg) are included in the repository. While unlikely to be malicious, large binary files in repositories can be exploited for supply chain attacks. Fix: Consider hosting benchmark assets separately or use Git LFS (Large File Storage). Verify file integrity with checksums in documentation.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · emmett-framework/granian — RepoPilot