Skip to content

v0.4.0 Release Notes¶

Release Date: August 2025

This release focuses on security and production readiness. Cello v0.4.0 adds JWT authentication, configurable rate limiting, secure cookie sessions, automatic security headers, multi-process cluster mode, and native TLS support -- everything needed to deploy Cello behind no reverse proxy at all if desired.


Highlights¶

  • JWT Authentication -- Issue, verify, and refresh JSON Web Tokens entirely in Rust
  • Rate Limiting -- Token bucket and sliding window algorithms with per-route configuration
  • Session Management -- Encrypted cookie sessions with automatic rotation
  • Security Headers -- CSP, HSTS, X-Frame-Options, and more applied automatically
  • Cluster Mode -- Multi-process workers sharing a single port
  • TLS Support -- Native HTTPS via rustls without an external proxy

New Features¶

JWT Authentication¶

Full JWT support powered by the jsonwebtoken crate with constant-time token comparison via subtle.

from cello import App, JwtConfig

app = App()

app.enable_jwt(JwtConfig(
    secret="your-secret-key",
    algorithm="HS256",
    expiry=3600,          # 1 hour
    issuer="cello-app",
    refresh_expiry=86400, # 24 hours
))

@app.post("/login")
def login(request):
    user = authenticate(request.json())
    token = app.jwt.create_token({"sub": user.id, "role": user.role})
    return {"access_token": token}

@app.get("/protected")
def protected(request):
    claims = request.jwt_claims  # Populated by JWT middleware
    return {"user_id": claims["sub"]}

Supported algorithms: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384

Rate Limiting¶

Two built-in algorithms with per-route and global configuration.

from cello import App, RateLimitConfig

app = App()

# Global rate limit
app.enable_rate_limit(RateLimitConfig(
    requests=100,
    window=60,
    strategy="sliding_window",  # or "token_bucket"
))

Rate-limited responses include standard headers:

Header Description
X-RateLimit-Limit Maximum requests allowed
X-RateLimit-Remaining Requests remaining in window
X-RateLimit-Reset Seconds until window resets
Retry-After Seconds to wait (on 429 responses)

Session Management¶

Encrypted, signed cookie sessions with no external store required.

from cello import App, SessionConfig

app = App()

app.enable_sessions(SessionConfig(
    secret="session-encryption-key",
    cookie_name="cello_session",
    max_age=86400,
    http_only=True,
    secure=True,
    same_site="Lax",
))

@app.get("/dashboard")
def dashboard(request):
    visits = request.session.get("visits", 0) + 1
    request.session["visits"] = visits
    return {"visits": visits}

Security features:

  • AES-256-GCM encryption for session data
  • HMAC signature to detect tampering
  • Automatic key rotation when max_age expires
  • SameSite cookie attribute to prevent CSRF

Security Headers¶

Apply a comprehensive set of security headers to every response.

from cello import App, SecurityHeadersConfig

app = App()

app.enable_security_headers(SecurityHeadersConfig(
    hsts=True,
    hsts_max_age=31536000,
    hsts_include_subdomains=True,
    content_type_nosniff=True,
    frame_options="DENY",
    xss_protection=True,
    referrer_policy="strict-origin-when-cross-origin",
    csp="default-src 'self'; script-src 'self'",
))

Applied headers:

  • Strict-Transport-Security (HSTS)
  • X-Content-Type-Options: nosniff
  • X-Frame-Options
  • X-XSS-Protection
  • Referrer-Policy
  • Content-Security-Policy

Cluster Mode¶

Run multiple worker processes sharing a single port using SO_REUSEPORT.

app.run(
    host="0.0.0.0",
    port=8000,
    workers=4,  # Spawn 4 worker processes
)
# Or via CLI
python app.py --workers 4

Each worker runs its own Tokio runtime with independent event loops. This sidesteps the Python GIL by using separate processes while the Rust layer handles port sharing.

TLS Support¶

Native HTTPS without needing nginx or a load balancer in front.

from cello import App, TlsConfig

app = App()

app.run(
    host="0.0.0.0",
    port=443,
    tls=TlsConfig(
        cert_path="/etc/certs/cert.pem",
        key_path="/etc/certs/key.pem",
    ),
)

Powered by rustls 0.22 -- no OpenSSL dependency. Supports TLS 1.2 and 1.3 with modern cipher suites.


Improvements¶

Performance¶

  • 10% faster routing through improved radix tree traversal
  • Lower memory per connection with optimized buffer pooling
  • Faster JSON responses through pre-allocated serialization buffers

Developer Experience¶

  • --env CLI flag for switching between development and production modes
  • Automatic reload in development mode (watches Python files for changes)
  • Colored log output with request timing in debug mode

Breaking Changes¶

Middleware Registration Order¶

Middleware now executes in the order it is registered, rather than by internal priority. If you relied on implicit ordering, verify your middleware chain.

# v0.4.0: Middleware runs in registration order
app.enable_cors(...)       # Runs first
app.enable_rate_limit(...) # Runs second
app.enable_jwt(...)        # Runs third

Bug Fixes¶

  • Fixed OPTIONS preflight not bypassing rate limiting
  • Fixed blueprint middleware not inheriting parent app middleware
  • Fixed WebSocket upgrade failing when compression middleware was enabled
  • Fixed static file MIME type detection for .wasm and .mjs files
  • Fixed SSE connections not being cleaned up on client disconnect

Dependencies Added¶

Dependency Version Purpose
jsonwebtoken 9.0 JWT creation and verification
subtle 2.5 Constant-time token comparison
rustls 0.22 Native TLS implementation

Migration from v0.3.0¶

No application code changes required. New features are opt-in.

  1. Update your dependency:

    pip install --upgrade cello-framework
    

  2. Optionally enable new middleware:

    app.enable_jwt(JwtConfig(...))
    app.enable_rate_limit(RateLimitConfig(...))
    app.enable_sessions(SessionConfig(...))
    app.enable_security_headers(SecurityHeadersConfig(...))
    

  3. For production, consider adding TLS and cluster mode:

    app.run(host="0.0.0.0", port=443, workers=4, tls=TlsConfig(...))
    


Full Changelog¶

See the complete changelog for all changes in this release.