cello-framework 1.2.1__tar.gz → 1.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cello_framework-1.2.1 → cello_framework-1.2.3}/CLAUDE.md +4 -2
- {cello_framework-1.2.1 → cello_framework-1.2.3}/Cargo.lock +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.3}/Cargo.toml +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.3}/PKG-INFO +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/core/async.md +1 -2
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/core/blueprints.md +1 -2
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/middleware/overview.md +1 -2
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/security/overview.md +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/index.md +11 -10
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/overrides/main.html +2 -2
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/api/app.md +2 -2
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/index.md +4 -5
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v0.5.0.md +12 -8
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v0.6.0.md +10 -23
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v1.2.1.md +58 -0
- cello_framework-1.2.3/docs/releases/v1.2.2.md +131 -0
- cello_framework-1.2.3/docs/releases/v1.2.3.md +121 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/mkdocs.yml +2 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/pyproject.toml +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/__init__.py +97 -1
- cello_framework-1.2.3/python/cello/middleware.py +166 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/lib.rs +91 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/auth.rs +4 -4
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/csrf.rs +4 -3
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/guards.rs +2 -2
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/mod.rs +13 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/rate_limit.rs +5 -5
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/session.rs +3 -3
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/minijinja_engine.rs +5 -5
- {cello_framework-1.2.1 → cello_framework-1.2.3}/tests/test_cello.py +3 -3
- {cello_framework-1.2.1 → cello_framework-1.2.3}/tests/test_minijinja.py +1 -1
- {cello_framework-1.2.1 → cello_framework-1.2.3}/.github/workflows/ci.yml +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/.github/workflows/docs.yml +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/.github/workflows/publish.yml +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/.gitignore +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/CONTRIBUTING.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/ENTERPRISE_ROADMAP.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/LICENSE +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/PUBLISHING.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/README.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/README.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/benchmark.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/compare/README.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/compare/apps/__init__.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/compare/apps/blacksheep_app.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/compare/apps/cello_app.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/compare/apps/fastapi_app.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/compare/apps/robyn_app.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/compare/requirements.txt +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/compare/run_benchmarks.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/benchmarks/quick_bench.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/cello.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/community/code-of-conduct.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/community/contributing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/community/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/community/support.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/deployment/docker.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/deployment/kubernetes.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/deployment/service-mesh.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/integration/database.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/integration/graphql.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/integration/grpc.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/integration/message-queues.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/observability/health-checks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/observability/metrics.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/observability/opentelemetry.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/observability/tracing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/enterprise/roadmap.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/advanced/background-tasks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/advanced/file-storage.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/advanced/fullstack.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/advanced/graphql.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/advanced/microservices.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/advanced/realtime-dashboard.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/advanced/redis-caching.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/basic/database.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/basic/forms.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/basic/hello-world.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/basic/jwt-auth.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/basic/query-params.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/basic/rest-api.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/enterprise/api-gateway.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/enterprise/event-sourcing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/enterprise/health-checks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/enterprise/multi-tenant.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/enterprise/oauth2.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/enterprise/rate-limiting.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/adaptive-rate-limiting.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/advanced-middleware.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/advanced-patterns.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/all-features.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/api-protocols.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/async-handlers.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/auto-openapi.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/blueprints-advanced.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/circuit-breaker.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/cluster-demo.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/complete-showcase.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/comprehensive-demo.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/database-demo.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/dto-validation.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/enterprise-config.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/guards.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/hello-world.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/lifecycle-hooks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/middleware-demo.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/minijinja-advanced.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/minijinja-basic.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/minijinja-blog.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/minijinja-emails.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/minijinja-forms.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/minijinja-macros.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/security.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/simple-api.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/smart-caching.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/examples/real/streaming-sse.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/advanced/background-tasks.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/advanced/dependency-injection.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/advanced/dto-validation.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/advanced/file-uploads.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/advanced/static-files.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/advanced/templates.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/core/requests.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/core/responses.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/core/routing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/middleware/caching.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/middleware/circuit-breaker.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/middleware/compression.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/middleware/cors.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/middleware/logging.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/middleware/rate-limiting.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/minijinja-templates.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/realtime/sse.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/realtime/websocket.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/security/authentication.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/security/csrf.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/security/guards.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/security/headers.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/security/jwt.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/security/sessions.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/features/templates.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/getting-started/configuration.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/getting-started/first-app.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/getting-started/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/getting-started/installation.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/getting-started/project-structure.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/getting-started/quickstart.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/includes/abbreviations.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/javascripts/extra.js +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/guides/best-practices.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/guides/deployment.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/guides/error-handling.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/guides/performance.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/guides/testing.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/patterns/cqrs.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/patterns/event-driven.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/patterns/repository.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/patterns/service-layer.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/tutorials/auth-system.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/tutorials/chat-app.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/tutorials/microservices.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/learn/tutorials/rest-api.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/logo-full.png +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/logo-icon.svg +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/logo.jpg +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/logo.svg +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/api/blueprint.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/api/context.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/api/guards.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/api/middleware.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/api/request.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/api/response.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/cli.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/config/middleware.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/config/security.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/config/server.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/reference/errors.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/changelog.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/index.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/migration.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v0.10.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v0.3.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v0.4.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v0.7.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v0.8.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v0.9.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v1.0.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v1.0.1.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v1.1.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/releases/v1.2.0.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/requirements.txt +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/stylesheets/extra.css +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/docs/tags.md +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/adaptive_rate_limit.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/advanced.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/advanced_middleware.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/advanced_patterns_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/all_features_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/api_protocols_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/async_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/auto_openapi_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/circuit_breaker.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/cluster_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/complete_showcase.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/comprehensive_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/database_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/dto_validation.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/enterprise.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/guards.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/hello.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/lifecycle_hooks.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/middleware_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/minijinja_advanced.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/minijinja_basic.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/minijinja_blog.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/minijinja_emails.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/minijinja_forms.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/minijinja_macros.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/security.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/simple_api.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/smart_caching.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/examples/streaming_demo.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/cqrs.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/database.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/eventsourcing.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/graphql.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/grpc.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/guards.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/messaging.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/saga.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/python/cello/validation.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/arena.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/background.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/blueprint.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/context.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/dependency.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/dto.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/error.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/handler.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/http_client.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/json.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/lifecycle.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/body_limit.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/cache.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/circuit_breaker.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/cors.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/cqrs.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/database.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/etag.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/eventsourcing.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/exception_handler.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/graphql.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/grpc.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/health.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/messaging.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/prometheus.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/redis.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/request_id.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/saga.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/security.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/static_files.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/middleware/telemetry.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/multipart.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/openapi.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/request/mod.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/request/multipart_streaming.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/request/parsing.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/response/mod.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/response/streaming.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/response/xml.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/router.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/routing/constraints.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/routing/mod.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/server/cluster.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/server/mod.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/server/protocols.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/sse.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/template.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/timeout.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/src/websocket.rs +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/tests/verify_adaptive.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/tests/verify_caching.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/tests/verify_circuit_breaker.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/tests/verify_dto.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/tests/verify_guards_impl.py +0 -0
- {cello_framework-1.2.1 → cello_framework-1.2.3}/tests/verify_lifecycle.py +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**Cello** is an ultra-fast, Rust-powered Python async web framework designed to achieve C-level performance on the hot path while maintaining Python's developer experience. It's the successor to frameworks like FastAPI, Robyn, and Litestar, combining their best features with pure Rust implementation for maximum performance.
|
|
6
6
|
|
|
7
|
-
**Version:** 1.
|
|
7
|
+
**Version:** 1.2.3
|
|
8
8
|
**License:** MIT
|
|
9
9
|
**Python Requirement:** 3.12+
|
|
10
10
|
**Author:** Jagadeesh Katla
|
|
@@ -346,7 +346,9 @@ def handle_value_error(request, exc):
|
|
|
346
346
|
|
|
347
347
|
## Version History
|
|
348
348
|
|
|
349
|
-
- **v1.2.
|
|
349
|
+
- **v1.2.3**: Full middleware Python API — `cello.middleware` module with `JwtAuth`, `BasicAuth`, `ApiKeyAuth`, `CsrfConfig`, `AdaptiveRateLimitConfig`; `app.use()` dispatcher; 6 new `enable_*` methods on App (`enable_jwt`, `enable_session`, `enable_security_headers`, `enable_csrf`, `enable_basic_auth`, `enable_api_key`); all docs import paths corrected (`from cello import RoleGuard` not `cello.guards`)
|
|
350
|
+
- **v1.2.2**: Security & bug fixes — CSRF `HttpOnly` on double-submit cookie (critical, broke all AJAX CSRF); all middleware `skip_path` prefix bypass fixed via `path_matches_skip()` helper; `FixedWindowStore` window_start never updated after reset; unused `mut` cleaned in minijinja tests
|
|
351
|
+
- **v1.2.1**: Bug fixes — server port never bound (`pyo3_asyncio` replaced with native `py.allow_threads` + `tokio::block_on`); `ProblemDetails` was missing from Python module export; `And`/`Or` guards now accept both `*args` and list styles; CSRF `HttpOnly` removed from double-submit cookie (JS must read it); `FixedWindowStore` window_start never updated after reset; all middleware `skip_path` used raw `starts_with` allowing prefix bypass; doc corrections (`type_uri` not `type_url`)
|
|
350
352
|
- **v1.2.0**: Bug fixes (shutdown coroutine never awaited, KeyboardInterrupt in shutdown handler, `request.redis` AttributeError); Redis Lua scripting (`eval`, `evalsha`, `script_load`); Rust-native `AsyncClient` backed by `reqwest + Tokio` — GIL never held during HTTP I/O, HTTP/2, gzip, rustls
|
|
351
353
|
- **v1.1.0**: MiniJinja Jinja2-compatible template engine (`MiniJinjaEngine`, `App.enable_templates()`, `App.render()`, `App.render_string()`); minijinja 2 Rust crate; HTML auto-escaping; globals; 47 new tests; 6 examples
|
|
352
354
|
- **v1.0.1**: Cross-platform fixes (Windows multi-worker, signal handling, UNC paths; ARM JSON fallback; Linux-only CPU affinity), async compatibility fixes (handler validation, guards, cache decorator, blueprints), guards and database exports in `__all__`
|
|
@@ -254,8 +254,7 @@ All Python-side decorators and wrappers in Cello automatically detect whether a
|
|
|
254
254
|
| Pydantic validation | Yes | Validates the request body, then awaits the handler |
|
|
255
255
|
|
|
256
256
|
```python
|
|
257
|
-
from cello import App, cache
|
|
258
|
-
from cello.guards import RoleGuard
|
|
257
|
+
from cello import App, cache, RoleGuard
|
|
259
258
|
from pydantic import BaseModel
|
|
260
259
|
|
|
261
260
|
app = App()
|
|
@@ -183,8 +183,7 @@ app.register_blueprint(v2)
|
|
|
183
183
|
Apply middleware to all routes within a blueprint. Async handlers work seamlessly with blueprint-level middleware and per-route guards:
|
|
184
184
|
|
|
185
185
|
```python
|
|
186
|
-
from cello import App, Blueprint
|
|
187
|
-
from cello.guards import RoleGuard, Authenticated
|
|
186
|
+
from cello import App, Blueprint, RoleGuard, Authenticated
|
|
188
187
|
|
|
189
188
|
# Public blueprint -- no auth required
|
|
190
189
|
public_bp = Blueprint("/public")
|
|
@@ -172,8 +172,7 @@ app.use(ApiKeyAuth(keys={"key1": "service-a"}, header="X-API-Key"))
|
|
|
172
172
|
All Python-side middleware wrappers -- including the `@cache` decorator, guard wrappers, and Pydantic validation -- fully support async handlers. Each wrapper uses `inspect.iscoroutinefunction()` to detect async handlers at decoration time and generates the appropriate sync or async wrapper. This means you can freely use `async def` handlers with any combination of caching, guards, and validation without encountering unawaited coroutine issues.
|
|
173
173
|
|
|
174
174
|
```python
|
|
175
|
-
from cello import App, cache
|
|
176
|
-
from cello.guards import RoleGuard
|
|
175
|
+
from cello import App, cache, RoleGuard
|
|
177
176
|
from pydantic import BaseModel
|
|
178
177
|
|
|
179
178
|
class Item(BaseModel):
|
|
@@ -261,7 +261,7 @@ app.use(api_auth)
|
|
|
261
261
|
Control access with composable guards:
|
|
262
262
|
|
|
263
263
|
```python
|
|
264
|
-
from cello
|
|
264
|
+
from cello import RoleGuard, PermissionGuard, And, Or
|
|
265
265
|
|
|
266
266
|
# Simple role check
|
|
267
267
|
admin_only = RoleGuard(["admin"])
|
|
@@ -37,7 +37,7 @@ hide:
|
|
|
37
37
|
[:material-package-variant: PyPI](https://pypi.org/project/cello-framework/){ .md-button }
|
|
38
38
|
|
|
39
39
|
<div class="hero-badges">
|
|
40
|
-
<code class="badge-version">v1.2.
|
|
40
|
+
<code class="badge-version">v1.2.3</code>
|
|
41
41
|
<code class="badge-tests">441 tests passing</code>
|
|
42
42
|
<code class="badge-license">MIT License</code>
|
|
43
43
|
<code class="badge-python">Python 3.12+</code>
|
|
@@ -178,8 +178,7 @@ hide:
|
|
|
178
178
|
=== ":material-api: REST API"
|
|
179
179
|
|
|
180
180
|
```python
|
|
181
|
-
from cello import App, Response, Blueprint
|
|
182
|
-
from cello.guards import RoleGuard
|
|
181
|
+
from cello import App, Response, Blueprint, RoleGuard
|
|
183
182
|
|
|
184
183
|
app = App()
|
|
185
184
|
api = Blueprint("api", url_prefix="/api/v1")
|
|
@@ -371,21 +370,23 @@ How Cello stacks up against popular Python web frameworks (4 workers, 5 processe
|
|
|
371
370
|
|
|
372
371
|
<!-- ===== WHAT'S NEW ===== -->
|
|
373
372
|
|
|
374
|
-
## :material-creation: What's New in v1.2.
|
|
373
|
+
## :material-creation: What's New in v1.2.3
|
|
375
374
|
|
|
376
375
|
<div class="whats-new-box" markdown>
|
|
377
376
|
|
|
378
|
-
!!! tip "v1.2.
|
|
377
|
+
!!! tip "v1.2.3 -- Full Middleware Python API & Correct Import Paths"
|
|
379
378
|
|
|
380
|
-
Cello v1.2.
|
|
379
|
+
Cello v1.2.3 exposes the complete auth & security middleware suite to Python, and fixes all doc import errors.
|
|
381
380
|
|
|
382
|
-
- :material-
|
|
381
|
+
- :material-api: **`app.use(middleware)`** -- New universal dispatcher: `app.use(JwtAuth(...))`, `app.use(BasicAuth(...))`, `app.use(ApiKeyAuth(...))`, `app.use(CsrfConfig())`.
|
|
383
382
|
|
|
384
|
-
- :material-
|
|
383
|
+
- :material-package: **`cello.middleware` module** -- `from cello.middleware import JwtAuth, BasicAuth, ApiKeyAuth, CsrfConfig` now works.
|
|
385
384
|
|
|
386
|
-
- :material-
|
|
385
|
+
- :material-language-rust: **6 new Rust-backed methods** -- `enable_jwt()`, `enable_session()`, `enable_security_headers()`, `enable_csrf()`, `enable_basic_auth()`, `enable_api_key()` added to App.
|
|
387
386
|
|
|
388
|
-
|
|
387
|
+
- :material-import: **All import paths corrected** -- `from cello import RoleGuard` (not `from cello.guards`). All 9 affected doc pages fixed.
|
|
388
|
+
|
|
389
|
+
[:material-tag: Full Release Notes](releases/v1.2.3.md){ .md-button .md-button--primary }
|
|
389
390
|
[:material-book-open-variant: Migration Guide](releases/migration.md){ .md-button }
|
|
390
391
|
|
|
391
392
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
{% block announce %}
|
|
4
4
|
<span class="cello-announce">
|
|
5
|
-
Cello v1.2.
|
|
5
|
+
Cello v1.2.3 — Full middleware Python API (JwtAuth, BasicAuth, ApiKeyAuth, app.use())
|
|
6
6
|
<a href="{{ base_url }}/releases/" class="cello-announce-link">
|
|
7
7
|
Release notes →
|
|
8
8
|
</a>
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"operatingSystem": "Linux, macOS, Windows",
|
|
38
38
|
"programmingLanguage": ["Python", "Rust"],
|
|
39
39
|
"license": "https://opensource.org/licenses/MIT",
|
|
40
|
-
"softwareVersion": "1.2.
|
|
40
|
+
"softwareVersion": "1.2.3",
|
|
41
41
|
"author": {
|
|
42
42
|
"@type": "Person",
|
|
43
43
|
"name": "Jagadeesh Katla",
|
|
@@ -728,9 +728,9 @@ Add a global guard to all routes.
|
|
|
728
728
|
> *Since v0.5.0*
|
|
729
729
|
|
|
730
730
|
```python
|
|
731
|
-
from cello
|
|
731
|
+
from cello import Authenticated
|
|
732
732
|
|
|
733
|
-
app.add_guard(
|
|
733
|
+
app.add_guard(Authenticated())
|
|
734
734
|
```
|
|
735
735
|
|
|
736
736
|
| Parameter | Type | Description |
|
|
@@ -78,7 +78,7 @@ The building blocks of every Cello application -- from creating your app to hand
|
|
|
78
78
|
|
|
79
79
|
Role-based and permission-based access control. Composable guards that protect routes and blueprints.
|
|
80
80
|
|
|
81
|
-
**Key classes:** `RoleGuard` `PermissionGuard` `
|
|
81
|
+
**Key classes:** `RoleGuard` `PermissionGuard` `Authenticated` `And` `Or` `Not`
|
|
82
82
|
|
|
83
83
|
[:octicons-arrow-right-24: Guards Reference](api/guards.md)
|
|
84
84
|
|
|
@@ -166,8 +166,8 @@ The most commonly used classes and functions at a glance.
|
|
|
166
166
|
| [`Response`](api/response.md) | `cello` | HTTP response builder |
|
|
167
167
|
| [`Blueprint`](api/blueprint.md) | `cello` | Route grouping |
|
|
168
168
|
| [`Depends`](api/context.md) | `cello` | Dependency injection marker |
|
|
169
|
-
| [`RoleGuard`](api/guards.md) | `cello
|
|
170
|
-
| [`PermissionGuard`](api/guards.md) | `cello
|
|
169
|
+
| [`RoleGuard`](api/guards.md) | `cello` | Role-based access guard |
|
|
170
|
+
| [`PermissionGuard`](api/guards.md) | `cello` | Permission-based access guard |
|
|
171
171
|
| [`JwtConfig`](config/security.md) | `cello.middleware` | JWT authentication config |
|
|
172
172
|
| [`RateLimitConfig`](config/middleware.md) | `cello.middleware` | Rate limiting config |
|
|
173
173
|
| [`SessionConfig`](config/security.md) | `cello.middleware` | Session management config |
|
|
@@ -219,8 +219,7 @@ The most commonly used classes and functions at a glance.
|
|
|
219
219
|
=== "Guards & Auth"
|
|
220
220
|
|
|
221
221
|
```python
|
|
222
|
-
from cello import App
|
|
223
|
-
from cello.guards import RoleGuard, PermissionGuard
|
|
222
|
+
from cello import App, RoleGuard, PermissionGuard
|
|
224
223
|
from cello.middleware import JwtConfig, JwtAuth
|
|
225
224
|
|
|
226
225
|
app = App()
|
|
@@ -65,14 +65,18 @@ def profile(request, user=Depends(get_current_user)):
|
|
|
65
65
|
|
|
66
66
|
Composable access control guards that run before the handler. Guards inspect the request context and either allow or deny access.
|
|
67
67
|
|
|
68
|
+
!!! note "API names updated in v1.x"
|
|
69
|
+
`RoleGuard`, `PermissionGuard`, `AuthenticatedGuard`, `AndGuard`, `OrGuard`, `NotGuard` were the original v0.5.0 names.
|
|
70
|
+
Current names: `RoleGuard` → still `RoleGuard` (from `cello`), `PermissionGuard` → still `PermissionGuard` (from `cello`),
|
|
71
|
+
`AuthenticatedGuard` → `Authenticated`, `AndGuard` → `And`, `OrGuard` → `Or`, `NotGuard` → `Not`.
|
|
72
|
+
|
|
68
73
|
```python
|
|
69
|
-
from cello import App
|
|
70
|
-
from cello.guards import RoleGuard, PermissionGuard, AuthenticatedGuard
|
|
74
|
+
from cello import App, RoleGuard, PermissionGuard, Authenticated
|
|
71
75
|
|
|
72
76
|
app = App()
|
|
73
77
|
|
|
74
78
|
# Require authentication
|
|
75
|
-
@app.get("/dashboard", guards=[
|
|
79
|
+
@app.get("/dashboard", guards=[Authenticated()])
|
|
76
80
|
def dashboard(request):
|
|
77
81
|
return {"welcome": request.context.get("user_id")}
|
|
78
82
|
|
|
@@ -90,18 +94,18 @@ def delete_user(request):
|
|
|
90
94
|
**Composable logic with And, Or, Not:**
|
|
91
95
|
|
|
92
96
|
```python
|
|
93
|
-
from cello
|
|
97
|
+
from cello import And, Or, Not, RoleGuard, PermissionGuard, Authenticated
|
|
94
98
|
|
|
95
99
|
# Admin OR has special permission
|
|
96
|
-
flexible =
|
|
100
|
+
flexible = Or([
|
|
97
101
|
RoleGuard(["admin"]),
|
|
98
102
|
PermissionGuard(["elevated:access"]),
|
|
99
103
|
])
|
|
100
104
|
|
|
101
105
|
# Authenticated AND not banned
|
|
102
|
-
safe =
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
safe = And([
|
|
107
|
+
Authenticated(),
|
|
108
|
+
Not(RoleGuard(["banned"])),
|
|
105
109
|
])
|
|
106
110
|
|
|
107
111
|
@app.get("/resource", guards=[flexible])
|
|
@@ -56,17 +56,10 @@ def create_user(request):
|
|
|
56
56
|
Rate limits now adjust based on server health:
|
|
57
57
|
|
|
58
58
|
```python
|
|
59
|
-
from cello
|
|
60
|
-
|
|
61
|
-
config = AdaptiveRateLimitConfig(
|
|
62
|
-
base_requests=100,
|
|
63
|
-
window=60,
|
|
64
|
-
cpu_threshold=0.8, # Reduce limits at 80% CPU
|
|
65
|
-
memory_threshold=0.9, # Reduce limits at 90% memory
|
|
66
|
-
latency_threshold=100, # Reduce limits if latency > 100ms
|
|
67
|
-
min_requests=10 # Never go below 10 req/min
|
|
68
|
-
)
|
|
59
|
+
from cello import App, RateLimitConfig
|
|
69
60
|
|
|
61
|
+
# Adaptive rate limiting (adjusts based on server load)
|
|
62
|
+
config = RateLimitConfig.adaptive(capacity=100, refill_rate=10)
|
|
70
63
|
app.enable_rate_limit(config)
|
|
71
64
|
```
|
|
72
65
|
|
|
@@ -109,18 +102,15 @@ Invalid requests return RFC 7807 Problem Details:
|
|
|
109
102
|
Protect against cascading failures:
|
|
110
103
|
|
|
111
104
|
```python
|
|
112
|
-
from cello
|
|
105
|
+
from cello import App
|
|
113
106
|
|
|
114
|
-
|
|
115
|
-
failure_threshold=5,
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
app.enable_circuit_breaker(
|
|
108
|
+
failure_threshold=5, # Open after 5 failures
|
|
109
|
+
reset_timeout=30, # Try again after 30 seconds
|
|
110
|
+
half_open_target=3, # Allow 3 test requests in half-open state
|
|
118
111
|
)
|
|
119
112
|
|
|
120
|
-
circuit = CircuitBreaker(config)
|
|
121
|
-
|
|
122
113
|
@app.get("/external-api")
|
|
123
|
-
@circuit.protect
|
|
124
114
|
async def call_external(request):
|
|
125
115
|
return await external_service.fetch()
|
|
126
116
|
```
|
|
@@ -130,8 +120,7 @@ async def call_external(request):
|
|
|
130
120
|
Guards can now be applied at multiple levels:
|
|
131
121
|
|
|
132
122
|
```python
|
|
133
|
-
from cello import App, Blueprint
|
|
134
|
-
from cello.guards import RoleGuard, PermissionGuard
|
|
123
|
+
from cello import App, Blueprint, RoleGuard, PermissionGuard
|
|
135
124
|
|
|
136
125
|
# Controller-level guard
|
|
137
126
|
admin_bp = Blueprint("/admin", guards=[RoleGuard(["admin"])])
|
|
@@ -181,9 +170,7 @@ The cache middleware configuration has changed:
|
|
|
181
170
|
app.enable_cache(max_age=300)
|
|
182
171
|
|
|
183
172
|
# After (v0.6.0)
|
|
184
|
-
|
|
185
|
-
config = CacheConfig(default_ttl=300, max_size=1000)
|
|
186
|
-
app.enable_cache(config)
|
|
173
|
+
app.enable_caching(ttl=300)
|
|
187
174
|
```
|
|
188
175
|
|
|
189
176
|
### Rate Limit Response
|
|
@@ -100,6 +100,64 @@ admin_or_editor = Or(RoleGuard(["admin"]), RoleGuard(["editor"]))
|
|
|
100
100
|
|
|
101
101
|
---
|
|
102
102
|
|
|
103
|
+
### CSRF Cookie Readable by JavaScript (Security)
|
|
104
|
+
|
|
105
|
+
**Symptom:** AJAX/SPA applications using CSRF protection failed all POST/PUT/DELETE requests with 403.
|
|
106
|
+
|
|
107
|
+
**Root cause:** The CSRF double-submit cookie was set with the `HttpOnly` flag. This prevents
|
|
108
|
+
JavaScript from reading the cookie value, making it impossible for the frontend to copy it into
|
|
109
|
+
the `X-CSRF-Token` header — which is the entire point of the double-submit pattern.
|
|
110
|
+
|
|
111
|
+
**Fix:** Removed `HttpOnly` from the CSRF cookie. The CSRF cookie is intentionally readable by
|
|
112
|
+
JavaScript; the session cookie (where sensitive data lives) remains `HttpOnly`.
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// Now works — JS can read the CSRF cookie and send it back
|
|
116
|
+
const csrf = document.cookie.match(/_csrf=([^;]+)/)?.[1];
|
|
117
|
+
fetch("/api/data", {
|
|
118
|
+
method: "POST",
|
|
119
|
+
headers: { "X-CSRF-Token": csrf },
|
|
120
|
+
body: JSON.stringify(data),
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### Rate Limiter Fixed-Window Counter Reset (Bug)
|
|
127
|
+
|
|
128
|
+
**Symptom:** After the first rate-limit window expired, the fixed-window counter reset to zero
|
|
129
|
+
on every single subsequent request, making rate limiting completely ineffective.
|
|
130
|
+
|
|
131
|
+
**Root cause:** When the time window rolled over, `entry.count` was reset to 0 but
|
|
132
|
+
`entry.window_start` was never updated to the new window. Every request in the new window
|
|
133
|
+
saw `window_start != current_window` and reset the counter again.
|
|
134
|
+
|
|
135
|
+
**Fix:** Update `entry.window_start = current_window` alongside the count reset.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### Auth Skip-Path Prefix Bypass (Security)
|
|
140
|
+
|
|
141
|
+
**Symptom:** Calling `skip_path("/health")` to exclude the health endpoint from authentication
|
|
142
|
+
also excluded `/healthz`, `/healthy`, and any other path starting with `/health`.
|
|
143
|
+
|
|
144
|
+
**Root cause:** Path matching used `request.path.starts_with(skip_path)` without checking that
|
|
145
|
+
the following character is `/` or end-of-string.
|
|
146
|
+
|
|
147
|
+
**Fix:** Path is now only skipped when it equals the pattern exactly or starts with
|
|
148
|
+
`{pattern}/`. Affects `JwtAuth`, `BasicAuth`, `ApiKeyAuth`, `RateLimitMiddleware`,
|
|
149
|
+
`SessionMiddleware`, `CsrfMiddleware`, and `GuardsMiddleware`.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# Before: /healthz would also be skipped (security bypass)
|
|
153
|
+
jwt.skip_path("/health")
|
|
154
|
+
|
|
155
|
+
# After: only /health and /health/* are skipped
|
|
156
|
+
jwt.skip_path("/health")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
103
161
|
## Upgrade
|
|
104
162
|
|
|
105
163
|
```bash
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: v1.2.2 Release Notes
|
|
3
|
+
description: Cello Framework v1.2.2 — Security & Bug Fixes
|
|
4
|
+
tags:
|
|
5
|
+
- v1.2.2
|
|
6
|
+
- Release Notes
|
|
7
|
+
- Security
|
|
8
|
+
- Bug Fixes
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Cello v1.2.2 — Security & Bug Fixes
|
|
12
|
+
|
|
13
|
+
**Release Date:** June 14, 2026
|
|
14
|
+
**License:** MIT
|
|
15
|
+
**Python:** 3.12+
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
Cello v1.2.2 is a patch release that fixes one critical security bug, one security
|
|
22
|
+
vulnerability, and two correctness bugs discovered during a thorough code review of v1.2.1.
|
|
23
|
+
All changes are fully backwards-compatible.
|
|
24
|
+
|
|
25
|
+
**Upgrade immediately** if you use CSRF protection or authentication middleware skip-paths.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Security Fixes
|
|
30
|
+
|
|
31
|
+
### CSRF Double-Submit Cookie Was `HttpOnly` (Critical)
|
|
32
|
+
|
|
33
|
+
**Symptom:** AJAX applications and SPAs using `CsrfMiddleware` received 403 on every
|
|
34
|
+
POST/PUT/DELETE request, even when the CSRF token was correctly set.
|
|
35
|
+
|
|
36
|
+
**Root cause:** The CSRF double-submit cookie was set with `HttpOnly`, which prevents
|
|
37
|
+
JavaScript from reading cookie values. The double-submit pattern *requires* JavaScript
|
|
38
|
+
to read the cookie and copy its value into the `X-CSRF-Token` request header. With
|
|
39
|
+
`HttpOnly` set, this was impossible — every state-changing request failed CSRF validation.
|
|
40
|
+
|
|
41
|
+
**Fix:** Removed `HttpOnly` from the CSRF token cookie. The CSRF cookie is intentionally
|
|
42
|
+
readable by JavaScript (that is the design). Your session cookie, which holds sensitive
|
|
43
|
+
data, remains `HttpOnly`.
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// Now works — JavaScript can read the CSRF cookie
|
|
47
|
+
const csrf = document.cookie.match(/_csrf=([^;]+)/)?.[1];
|
|
48
|
+
|
|
49
|
+
fetch("/api/submit", {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: { "X-CSRF-Token": csrf, "Content-Type": "application/json" },
|
|
52
|
+
body: JSON.stringify({ name: "Alice" }),
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### Auth Middleware `skip_path` Allowed Prefix Bypass (Security)
|
|
59
|
+
|
|
60
|
+
**Symptom:** Calling `jwt.skip_path("/health")` to exclude the health-check endpoint
|
|
61
|
+
from authentication also excluded `/healthz`, `/healthy`, `/health-admin`, and any
|
|
62
|
+
other path whose string representation started with `/health`.
|
|
63
|
+
|
|
64
|
+
**Root cause:** All middleware used `request.path.starts_with(skip_path)` with no
|
|
65
|
+
boundary check. An attacker aware of a skipped path could craft a URL that bypasses
|
|
66
|
+
authentication.
|
|
67
|
+
|
|
68
|
+
**Affected middleware:** `JwtAuth`, `BasicAuth`, `ApiKeyAuth`, `RateLimitMiddleware`,
|
|
69
|
+
`SessionMiddleware`, `CsrfMiddleware`, `GuardsMiddleware`.
|
|
70
|
+
|
|
71
|
+
**Fix:** Paths are now only skipped when:
|
|
72
|
+
|
|
73
|
+
- The path **exactly equals** the pattern, or
|
|
74
|
+
- The path **starts with** `{pattern}/` (sub-path match).
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# Before: /healthz was also skipped — security bypass
|
|
78
|
+
jwt_auth.skip_path("/health")
|
|
79
|
+
|
|
80
|
+
# After: only /health and /health/* are skipped
|
|
81
|
+
jwt_auth.skip_path("/health")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Bug Fixes
|
|
87
|
+
|
|
88
|
+
### Rate Limiter Fixed-Window Counter Broke After First Window
|
|
89
|
+
|
|
90
|
+
**Symptom:** After the first time-window expired, the `FixedWindowStore` rate limiter
|
|
91
|
+
stopped enforcing limits entirely — every request was allowed regardless of how many
|
|
92
|
+
had been made.
|
|
93
|
+
|
|
94
|
+
**Root cause:** When the window rolled over, the per-client entry's `count` was reset
|
|
95
|
+
to 0 but `window_start` was never updated to the new window ID. Every subsequent
|
|
96
|
+
request also saw `window_start != current_window` and reset the counter to 0 again,
|
|
97
|
+
making the limiter permanently ineffective after the first window.
|
|
98
|
+
|
|
99
|
+
**Fix:** `window_start` is now updated to `current_window` alongside the count reset.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### Unused `mut` Warnings in Template Engine Tests
|
|
104
|
+
|
|
105
|
+
Five test functions in `minijinja_engine.rs` declared `let mut env = Environment::new()`
|
|
106
|
+
where `mut` was not needed (the variable was never mutated after construction). These
|
|
107
|
+
were cleaned up to eliminate Clippy warnings.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Files Changed
|
|
112
|
+
|
|
113
|
+
| File | Change |
|
|
114
|
+
|------|--------|
|
|
115
|
+
| `src/middleware/csrf.rs` | Remove `HttpOnly` from CSRF cookie |
|
|
116
|
+
| `src/middleware/mod.rs` | Add `path_matches_skip()` shared helper |
|
|
117
|
+
| `src/middleware/auth.rs` | Use `path_matches_skip()` in JWT/Basic/ApiKey |
|
|
118
|
+
| `src/middleware/session.rs` | Use `path_matches_skip()` |
|
|
119
|
+
| `src/middleware/rate_limit.rs` | Use `path_matches_skip()`; fix `window_start` reset |
|
|
120
|
+
| `src/middleware/guards.rs` | Use `path_matches_skip()` |
|
|
121
|
+
| `src/minijinja_engine.rs` | Remove unused `mut` from test bindings |
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Upgrade
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pip install --upgrade cello-framework==1.2.2
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Drop-in replacement for v1.2.1. No API changes.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: v1.2.3 Release Notes
|
|
3
|
+
description: Cello Framework v1.2.3 — Full Middleware Python API & Docs Fixes
|
|
4
|
+
tags:
|
|
5
|
+
- v1.2.3
|
|
6
|
+
- Release Notes
|
|
7
|
+
- New Features
|
|
8
|
+
- Bug Fixes
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Cello v1.2.3 — Full Middleware Python API & Docs Fixes
|
|
12
|
+
|
|
13
|
+
**Release Date:** June 14, 2026
|
|
14
|
+
**License:** MIT
|
|
15
|
+
**Python:** 3.12+
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
Cello v1.2.3 exposes the complete authentication and security middleware suite to
|
|
22
|
+
Python — functionality that existed in Rust since v0.4.0 but had no Python API.
|
|
23
|
+
It also fixes all incorrect import paths across the documentation.
|
|
24
|
+
|
|
25
|
+
Drop-in upgrade from v1.2.2. No breaking changes.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## New Features
|
|
30
|
+
|
|
31
|
+
### Full Middleware Python API
|
|
32
|
+
|
|
33
|
+
All authentication and security middleware is now accessible from Python.
|
|
34
|
+
|
|
35
|
+
#### `app.use(middleware)` — universal middleware dispatcher
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from cello import App, JwtConfig
|
|
39
|
+
from cello.middleware import JwtAuth, BasicAuth, ApiKeyAuth, CsrfConfig
|
|
40
|
+
|
|
41
|
+
app = App()
|
|
42
|
+
|
|
43
|
+
# JWT authentication
|
|
44
|
+
app.use(JwtAuth(JwtConfig(secret="your-32-byte-secret-key!", algorithm="HS256")))
|
|
45
|
+
|
|
46
|
+
# Basic auth with credential dict
|
|
47
|
+
app.use(BasicAuth(credentials={"admin": "s3cr3t"}, realm="My API"))
|
|
48
|
+
|
|
49
|
+
# API key validation
|
|
50
|
+
app.use(ApiKeyAuth(keys={"key-abc": "service-a", "key-xyz": "service-b"}))
|
|
51
|
+
|
|
52
|
+
# CSRF protection
|
|
53
|
+
app.use(CsrfConfig())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### New `App` enable methods
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
app.enable_jwt(JwtConfig(secret="...", algorithm="HS256"), skip_paths=["/health"])
|
|
60
|
+
app.enable_session(SessionConfig(cookie_name="sid", max_age=3600))
|
|
61
|
+
app.enable_security_headers(strict=False)
|
|
62
|
+
app.enable_csrf()
|
|
63
|
+
app.enable_basic_auth(credentials={"admin": "secret"}, realm="Protected")
|
|
64
|
+
app.enable_api_key(keys={"key123": "service-a"}, header="X-API-Key")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### New `cello.middleware` module
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from cello.middleware import (
|
|
71
|
+
JwtAuth, # JWT auth wrapper for app.use()
|
|
72
|
+
BasicAuth, # Basic auth wrapper for app.use()
|
|
73
|
+
ApiKeyAuth, # API key auth wrapper for app.use()
|
|
74
|
+
CsrfConfig, # CSRF middleware config for app.use()
|
|
75
|
+
AdaptiveRateLimitConfig, # Convenience wrapper for RateLimitConfig.adaptive()
|
|
76
|
+
# Also re-exported from cello for convenience:
|
|
77
|
+
JwtConfig,
|
|
78
|
+
SessionConfig,
|
|
79
|
+
SecurityHeadersConfig,
|
|
80
|
+
RateLimitConfig,
|
|
81
|
+
CSP,
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Docs Fixes
|
|
88
|
+
|
|
89
|
+
All documentation pages now use correct import paths. Previously, several pages
|
|
90
|
+
showed imports that raised `ImportError` at runtime.
|
|
91
|
+
|
|
92
|
+
| Wrong (raised ImportError) | Correct |
|
|
93
|
+
|---------------------------|---------|
|
|
94
|
+
| `from cello.guards import RoleGuard` | `from cello import RoleGuard` |
|
|
95
|
+
| `from cello.guards import PermissionGuard` | `from cello import PermissionGuard` |
|
|
96
|
+
| `from cello.guards import AuthenticatedGuard` | `from cello import Authenticated` |
|
|
97
|
+
| `from cello.guards import AndGuard` | `from cello import And` |
|
|
98
|
+
| `from cello.middleware import CircuitBreaker` | `app.enable_circuit_breaker(...)` |
|
|
99
|
+
| `from cello.middleware import CacheConfig` | `app.enable_caching(ttl=...)` |
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Files Changed
|
|
104
|
+
|
|
105
|
+
| File | Change |
|
|
106
|
+
|------|--------|
|
|
107
|
+
| `src/lib.rs` | Add `enable_jwt`, `enable_session`, `enable_security_headers`, `enable_csrf`, `enable_basic_auth`, `enable_api_key` to Rust `Cello` struct |
|
|
108
|
+
| `python/cello/middleware.py` | **New file** — `JwtAuth`, `BasicAuth`, `ApiKeyAuth`, `CsrfConfig`, `AdaptiveRateLimitConfig` |
|
|
109
|
+
| `python/cello/__init__.py` | Add `App.use()` dispatcher and 6 new `enable_*` methods |
|
|
110
|
+
| `docs/` | 9 pages updated with correct import paths |
|
|
111
|
+
| `docs/overrides/main.html` | Announcement bar updated to v1.2.3 |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Upgrade
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pip install --upgrade cello-framework==1.2.3
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Drop-in replacement for v1.2.2. No API changes.
|