Overview
Chirp apps run on Pounce, a production-grade ASGI server with enterprise features built-in.
Phase 5 (Automatic)
- HTTP response and WebSocket compression — ordinary responses and WebSocket messages can be compressed; Pounce intentionally avoids compressing
text/event-stream - HTTP/2 support — Multiplexed streams, server push
- Graceful shutdown — Finishes active requests on SIGTERM
- Zero-downtime reload —
kill -SIGUSR1for hot code updates - OpenTelemetry — Distributed tracing (configurable)
Phase 6 (Configurable)
- Prometheus metrics —
/metricsendpoint for monitoring - Per-IP rate limiting — Token bucket algorithm
- Request queueing — Load shedding during traffic spikes
- Sentry integration — Error tracking and reporting
- Hot reload — Zero-downtime worker replacement
Quick Start
Development Mode
from chirp import App, AppConfig
app = App(AppConfig(debug=True))
@app.route("/")
def index():
return "Hello!"
app.run() # Single worker, auto-reload
Production Mode
from chirp import App, AppConfig
config = AppConfig(
debug=False,
secret_key="your-secret-key-here",
workers=4,
metrics_enabled=True,
rate_limit_enabled=True,
)
app = App(config=config)
@app.route("/")
def index():
return "Hello, Production!"
app.run() # Multi-worker, Phase 5 & 6 features
CLI Production Mode
chirp run myapp:app --production --workers 4 --metrics --rate-limit
Operator Preflight
Run Chirp and Pounce checks before deployment because they validate different contracts:
chirp check myapp:app --warnings-as-errors
pounce check --app myapp:app --config pounce.toml
chirp checkvalidates Chirp's hypermedia contracts: routes, templates,
blocks, OOB targets, forms, SSE wiring, and app-level checks.
pounce checkvalidates Pounce's server-facing inputs: import path, config
file, bind address, TLS files, worker settings, and related server options. If
you do not usepounce.toml, pass the same server flags you use at runtime:
pounce check --app myapp:app --host 0.0.0.0 --port 8000 --workers 4
Pounce-Native Config
pounce.toml is Pounce-native today. It is read by pounce serveand
pounce check; it is not read by app.run() or chirp run, which use
AppConfigplus Chirp CLI flags.
pounce config schema --output-format toml-template
pounce config schema --output-format json
pounce config show --config pounce.toml --output-format toml
pounce serve --app myapp:app --config pounce.toml
Use toml-template to generate a starting pounce.toml, jsonfor tooling,
andconfig showto inspect the resolved Pounce config after file and CLI
overrides are merged.
Worker Lifecycle Hooks
Use@app.on_worker_startup and @app.on_worker_shutdownfor resources that
must be created inside each production worker, such as async HTTP clients or
event-loop-bound database pools.
Worker lifecycle hooks require async workers in production:
config = AppConfig(debug=False, workers=4, worker_mode="async")
Pounce 0.7 sync workers do not emit worker lifecycle scopes. On free-threaded
Python, Pounce resolvesworker_mode="auto"to sync workers, so Chirp rejects
production launch when worker hooks are registered and the effective worker
mode is sync. If you do not register worker hooks,worker_mode="auto"remains
valid.
Worker startup failures are best-effort in Pounce 0.7 async workers: Pounce
logs the exception and continues serving. Put must-succeed application-wide
checks in@app.on_startup, and use a health check for dependencies that can
fail after startup. Fail-loud worker startup is tracked upstream in
pounce#65.
Realtime And Compression
SSE is Chirp's realtime contract. Pounce intentionally avoids compressing
text/event-streamresponses so event delivery is not buffered behind
compression windows. Useworker_mode="async"for apps with long-lived SSE
connections.
Pounce Introspection
Pounce 0.7 includes a server-level introspection endpoint at/_pounce/info,
but it is disabled by default and remains Pounce-native in Chirp today. Chirp
does not exposeAppConfigfields for Pounce introspection yet.
If you enable Pounce introspection throughpounce.toml or pounce serve
flags, treat it as an operations endpoint. Pounce handles it before the Chirp
app and before Chirp middleware, so do not rely on Chirp auth, CSRF, sessions,
or allowed-host middleware to protect it.
Bind introspection to loopback or a private admin network, and access it through a VPN, SSH tunnel, or port-forward. Do not expose it on a public internet interface.
Custom Port Checks
If your CLI checks whether a port is free before callingapp.run() (e.g. to avoid split-brain or show a clearer error), use SO_REUSEADDR in that check. Otherwise you'll block restarts when the port is in TIME_WAIT (30–120 seconds after shutdown). The server already uses SO_REUSEADDR; your check should match.
import socket
def is_port_in_use(host: str, port: int) -> bool:
"""Return True if another process is actively listening on host:port."""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
return False
except OSError:
return True
Without SO_REUSEADDR, the check fails when the port is in TIME_WAIT even though the server would bind successfully. When you can't identify the process holding the port, include a TIME_WAIT hint in your error message (e.g. "wait 30–60s or use a different port").
Docker
FROM python:3.14-slim
WORKDIR /app
COPY . .
RUN pip install bengal-chirp
CMD ["chirp", "run", "myapp:app", "--production", "--workers", "4"]
Configuration
| Config | Default | Description |
|---|---|---|
workers |
0(auto) |
Worker count (0 = CPU count) |
worker_mode |
"auto" |
Pounce worker execution mode; use"async"when registering worker lifecycle hooks |
metrics_enabled |
False |
Prometheus/metricsendpoint |
rate_limit_enabled |
False |
Per-IP rate limiting |
request_queue_enabled |
False |
Request queueing and load shedding |
sentry_dsn |
None |
Sentry error tracking |
ssl_certfile |
None |
TLS certificate (enables HTTP/2) |
ssl_keyfile |
None |
TLS private key |
Do not assume Pounce environment variable names are read by Chirp. If your
platform provides deployment variables, read them in your app code and build an
AppConfig, or start through pounce serve --config pounce.toml.
Chirp intentionally does not expose Pounce trusted proxy, compression, or
introspection settings throughAppConfigyet. Those fields are
security-facing and need a separate public API decision before adoption.
Full Guide
For detailed deployment instructions, TLS setup, Kubernetes, and advanced configuration, see the source for this published guide in the repository: