Skip to content

Releases: fireflyframework/fireflyframework-pyfly

v26.06.114

Choose a tag to compare

@github-actions github-actions released this 26 Jun 11:39
06861e0

v26.06.114 (2026-06-26)

A comprehensive security release: PyFly now aligns with RFC 9700 (OAuth 2.0
Security Best Current Practice)
and OAuth 2.1, reaches broad Spring
Security parity
, and ships a complete OAuth 2.1 / OpenID Connect stack — followed
by a full security documentation refresh.

Added

  • OAuth 2.1 authorization server. AuthorizationServer implements the
    authorization_code grant (single-use codes, exact redirect-URI matching,
    mandatory PKCE S256, code-reuse → token revocation) alongside
    client_credentials and refresh_token. It issues OIDC id_tokens for the
    openid scope, supports symmetric (HS*) and asymmetric (RS*/ES*/PS*)
    signing with a published JWK set (jwks()), and is fronted by
    AuthorizationServerEndpoints: GET /oauth2/authorize, POST /oauth2/par,
    POST /oauth2/token, POST /oauth2/introspect, POST /oauth2/revoke,
    POST /oauth2/register, GET /oauth2/jwks, and the
    /.well-known/oauth-authorization-server + /.well-known/openid-configuration
    discovery documents. Includes Dynamic Client Registration (RFC 7591), Pushed
    Authorization Requests (RFC 9126), JWT-Secured Authorization Requests (RFC 9101),
    token introspection (RFC 7662), revocation (RFC 7009), and AS metadata (RFC 8414).
  • Sender-constrained tokens. DPoP (RFC 9449) and mTLS (RFC 8705): the
    authorization server binds tokens via a cnf claim and the resource server
    enforces proof-of-possession (DPoPProofValidator, confirm_dpop_binding,
    confirm_mtls_binding) when
    pyfly.security.oauth2.resource-server.enforce-sender-constraints=true.
  • Opaque-token introspection. OpaqueTokenIntrospector validates non-JWT
    access tokens via a remote RFC 7662 endpoint, mapping claims identically to the
    JWKS validator.
  • Authentication mechanisms (Spring parity). A UserDetailsService SPI
    (InMemoryUserDetailsService, SQLAlchemy-backed SqlUserDetailsService), an
    AuthenticationManager (ProviderManager + DaoAuthenticationProvider, with
    username-enumeration-resistant timing and credential erasure), and config-driven
    form login (FormLoginFilter), HTTP Basic (HttpBasicAuthenticationFilter),
    X.509 client-certificate auth (X509AuthenticationFilter), generic logout
    (LogoutFilter), and run-as impersonation (SwitchUserFilter).
  • Password encoders. Pbkdf2PasswordEncoder, ScryptPasswordEncoder,
    Argon2PasswordEncoder (Argon2id; pip install pyfly[argon2]), and a
    DelegatingPasswordEncoder ({id}-prefixed, with upgrade_encoding for
    transparent on-login migration) plus create_delegating_password_encoder().
  • Authorization breadth. HTTP-method-scoped URL rules
    (request_matchers(..., methods=...)), method-security collection filtering
    (@pre_filter / @post_filter binding filterObject), and a PermissionEvaluator
    SPI backing ACL-style hasPermission(target, perm).
  • RFC 9207 issuer (iss) validation in the OAuth2 client callback (mix-up
    attack defense; ClientRegistration.require_iss).
  • Configuration. New keys under pyfly.security.{http-basic,form-login,logout}.*,
    pyfly.security.password.delegating.enabled, pyfly.security.csrf.cookie-gated,
    pyfly.security.oauth2.authorization-server.audience,
    pyfly.security.oauth2.resource-server.{enforce-sender-constraints,mtls-cert-header},
    per-registration use-pkce / require-iss, and pyfly.idp.allow-password-grant.
    New optional extra: pyfly[argon2].

Changed

  • PKCE is on by default for the OAuth2 authorization_code login flow and is
    always enforced for public clients (ClientRegistration.use_pkce now defaults
    True).
  • CSRF is enabled by default in cookie-gated mode (stateless / Bearer clients
    are unaffected); opt out with pyfly.security.csrf.enabled=false, or set
    pyfly.security.csrf.cookie-gated=false for strict enforcement.
  • client_credentials scope validation: a request for a scope the client is
    not registered for is now rejected with invalid_scope instead of being echoed.

Security

  • Signing-secret fail-fast. The composition root refuses to start when a
    token-signing secret is left at the built-in placeholder change-me-in-production,
    and requires ≥ 32-byte HMAC keys for HS* algorithms (RFC 7518 §3.2).
  • ROPC disabled by default. The IdP Resource Owner Password Credentials grant
    (grant_type=password) on the Keycloak / Cognito / Entra adapters is refused
    unless pyfly.idp.allow-password-grant=true (OAuth 2.1 / RFC 9700 §2.4).
  • Refresh-token reuse detection. Replaying an already-rotated refresh token
    revokes the entire token family.
  • Owner-scoped introspection & revocation. A client may only introspect /
    revoke its own tokens (RFC 7009 §2.1); empty client credentials are rejected.

Documentation

  • New OAuth 2.1 & OpenID Connect guide (docs/modules/oauth2.md). The Security
    guide gained Authentication Mechanisms, Security Headers, and Secure-by-Default
    & Hardening
    sections; the Spring comparison, IDP, and web-filters docs were
    updated; and PyFly by Example chapter 14 (English and Spanish) gained
    sections on form login, modern password hashing, the authorization server, and
    sender-constrained tokens.

Full Changelog: v26.06.113...v26.06.114

v26.06.113

Choose a tag to compare

@github-actions github-actions released this 17 Jun 16:08
332776e

v26.06.113 (2026-06-17)

Added

  • Server-layer observability. Observability is no longer only application-layer
    (the http_server_requests_seconds filter, tracing/correlation, process metrics):
    pyfly now emits metrics about the ASGI server itself across Uvicorn, Granian,
    and Hypercorn. A pure-ASGI ServerMetricsASGIMiddleware (the uniform primary
    source, running in every worker) emits server_active_connections,
    server_in_flight_requests, and server_requests_total; a ServerMetricsBinder
    bound from the in-worker ASGI lifespan emits server_workers,
    server_uptime_seconds, and server_started_total / server_stopped_total; and
    a best-effort ServerStatsPort surfaces Uvicorn's true socket count
    (server_native_connections) on the in-process serve_async path. Every meter is
    labeled server and worker_pid.
  • Correct multi-worker aggregation. With workers > 1, pyfly run enables
    prometheus_client multiprocess mode (sets PROMETHEUS_MULTIPROC_DIR before
    forking), so a single /actuator/prometheus scrape aggregates across all workers
    via MultiProcessCollector — this also fixes the previous per-worker gap for
    http_server_requests_*.
  • Live admin Observability dashboard. A new real-time Observability view
    (under Monitoring) shows server workers, uptime, active connections, in-flight
    requests, requests/sec, a per-worker breakdown, and worker lifecycle, with links
    to the Metrics and Traces views. Backed by GET /admin/api/observability and the
    observability SSE stream.
  • Configuration. New pyfly.server.observability.* keys — enabled
    (default true, activated by the web/core starters), sample-interval-seconds
    (5.0), and access-log (false, opt-in). Requires the observability extra
    (prometheus_client); degrades to a no-op without it.
  • Local observability stack. docker-compose.yml gained loopback-bound
    Prometheus + Grafana services (config in ops/prometheus/prometheus.yml) that
    scrape /actuator/prometheus.

Scope: gunicorn is intentionally not added (the stack stays async-only ASGI:
Granian > Uvicorn > Hypercorn), but the ServerStatsPort + multiprocess design is
gunicorn-ready for a future adapter.


Full Changelog: v26.06.112...v26.06.113

v26.06.112

Choose a tag to compare

@github-actions github-actions released this 16 Jun 20:13
872b5af

v26.06.112 (2026-06-16)

Changed

  • "PyFly by Example" — all diagrams redesigned. Every content figure in both
    the English and Spanish editions was rebuilt in a single, polished design
    language (consistent cards, gradient headers, numbered step flows, monospace
    code tokens, brand palette, vector iconography). This fixes real defects in the
    previous artwork — broken/tofu glyphs (font-dependent arrows, circled numbers
    and check marks) and clipped/overflowing content — by drawing all arrows,
    checkmarks and numbered badges as vector shapes and keeping figure text to
    ASCII/Latin-1 only.
  • Two new figures where they help most: Page / Pageable / Sort (Chapter 5)
    and Value Object vs Entity (Chapter 6). Both editions rebuilt and re-attached
    to the release.

Full Changelog: v26.06.111...v26.06.112

v26.06.111

Choose a tag to compare

@github-actions github-actions released this 16 Jun 19:42
ef200b3

v26.06.111 (2026-06-16)

Added

  • "PyFly by Example" — Spanish edition. The book is now published in Spanish
    (book/dist/pyfly-by-example-es.{epub,pdf}) alongside the English edition,
    built from a parallel book/manuscript-es/ manuscript via book.es.yaml. The
    book build (book/build/build.py) is now language-parameterized (--config,
    per-manifest manuscript_dir / output_basename / localized labels). Both
    editions are attached to the GitHub release.
  • Quick Start tutorial + step-by-step depth. A new "Build Lumen Step by Step"
    walkthrough takes the reader from an empty folder to a running, tested wallet
    feature; every chapter was deepened into a more granular, beginner-friendly
    tutorial. A dedication was added (EN + ES).

Fixed

  • pyfly new no longer scaffolds the removed pyfly.web.port key. The project
    template (pyfly.yaml.j2) now emits the port under server: as
    pyfly.server.port (Spring server.port parity); the legacy pyfly.web.port
    was removed in v26.06.102 and had been left as a dead key in freshly scaffolded
    applications.

Full Changelog: v26.06.110...v26.06.111

v26.06.110

Choose a tag to compare

@github-actions github-actions released this 16 Jun 16:12
f95cb1a

v26.06.110 (2026-06-16)

Fixed

  • The separate management port (actuator + admin) is now OPEN by default. When
    pyfly.management.server.port runs actuator/admin on a dedicated port, the
    app's user security filters (e.g. an HttpSecurity gate whose deny-all
    catch-all is scoped to the main app's URL space) were applied there too —
    rejecting /admin, /actuator/info, /actuator/metrics with 401/403
    while only /actuator/health (explicitly permitted) worked. The management
    port is a separate, typically-internal listener (Spring management.server.port
    parity) protected by network isolation, so it no longer applies the app's
    security filters by default. Opt back in with
    pyfly.management.security.enabled: true.

Full Changelog: v26.06.109...v26.06.110

v26.06.109

Choose a tag to compare

@github-actions github-actions released this 16 Jun 15:31
0357813

v26.06.109 (2026-06-16)

Fixed

  • CORS preflight is no longer rejected by the security gate. The
    CORSMiddleware is now the outermost middleware (ahead of the
    WebFilterChain that holds the HttpSecurity gate) on both the Starlette
    and FastAPI adapters. Previously the filter chain wrapped CORS, so a browser
    OPTIONS preflight (which carries no credentials) to a gated route was
    answered with 401 and without Access-Control-* headers — the browser
    then blocked the real request ("Load failed"/"Failed to fetch"). The preflight
    is now answered by CORS before the gate runs, and Access-Control-* headers
    are added to every response.

Full Changelog: v26.06.108...v26.06.109

v26.06.108

Choose a tag to compare

@github-actions github-actions released this 16 Jun 14:28
c566619

v26.06.108 (2026-06-16)

Added

  • The live actuator HealthAggregator is exposed on
    app.state.pyfly_health_aggregator.
    Consumers can now register extra
    health indicators after create_app (e.g. a readiness-only probe for an
    external dependency) without introspecting route closures. It is the same
    aggregator the live health routes use — whether actuator runs on the main app
    (shared management mode) or on the separate management port — so indicators
    added through it are reflected on /actuator/health in either mode.

Full Changelog: v26.06.107...v26.06.108

v26.06.107

Choose a tag to compare

@github-actions github-actions released this 16 Jun 14:01
52c2af3

v26.06.107 (2026-06-16)

Added

  • OAuth2 resource server: config-driven, multi-IdP, Spring-parity. The
    bearer-token resource server now works out of the box with Keycloak,
    Microsoft Entra ID (v1.0 + v2.0) and AWS Cognito via configuration
    alone (no subclassing), and reaches Spring-Security parity:
    • issuer-uri OIDC discovery — derive the JWKS endpoint + issuer from
      <issuer-uri>/.well-known/openid-configuration (alternative to jwks-uri).
    • Config-driven claim mapping (pyfly.security.oauth2.resource-server.*):
      principal-claim-names, authorities-claim-names, scope-claim-names,
      attribute-claims, authority-prefix. Claim names accept dotted paths
      with a * wildcard and are colon-safe, so authorities resolve from
      realm_access.roles, resource_access.*.roles (Keycloak), roles + groups
      (Entra), and cognito:groups (Cognito) with zero code.
    • audiences (a list; aud must match any) and validate-audience
      (disable for Cognito access tokens, which carry no aud).
    • Configurable algorithms, clock-skew-seconds, jwks-timeout-seconds,
      jwks-cache-seconds.
    • New typed ResourceServerProperties (@config_properties) and
      ClaimMappings.

Fixed

  • OAuth2 resource server — clock-skew leeway. JWT validation now allows 60s
    of clock skew by default (configurable). Previously a token whose iat/nbf
    was a few seconds ahead of the server clock — routine with real IdPs — was
    rejected as "not yet valid", causing intermittent 401s.
  • OAuth2 resource server — event-loop stall. The bearer filter now runs JWKS
    validation (which does blocking network I/O on a cache miss) in a worker thread
    (anyio.to_thread) instead of inline on the event loop.
  • OAuth2 resource server — multi-IdP claim coverage. Token-to-SecurityContext
    mapping previously read only realm_access.roles and scope/permissions,
    silently dropping Keycloak resource_access client roles, Entra groups /
    scp, Cognito cognito:groups, and any token attributes. All are now mapped
    (configurably).
  • OAuth2 resource server — case-insensitive Bearer scheme (RFC 7235): a
    bearer … / BEARER … Authorization header is now accepted.
  • OAuth2 resource server — opt-in strict rejection. New
    authenticate-error-mode: "401" rejects a present-but-invalid token at the
    filter with 401 + WWW-Authenticate: Bearer error="invalid_token" (RFC
    6750). Default remains "anonymous" (the gate decides) — no behavioural change
    unless opted in.

Full Changelog: v26.06.106...v26.06.107

v26.06.106

Choose a tag to compare

@github-actions github-actions released this 16 Jun 10:44
1ab27f0

v26.06.106 (2026-06-16)

Fixed

  • Admin dashboard: the Overview "Thread Count" gauge no longer renders the
    thread count as a percentage.
    The gauge widget (createGaugeChart) was a
    fixed 0–100 percentage meter — it clamped its value to 100 and appended a
    hard-coded % to the centre readout — but the Overview page feeds it the
    absolute active-thread count. A process with 8 threads therefore showed
    8%, and any count above 100 clamped to 100%. The gauge now accepts a
    max (the value that fills the arc) and a unit (the readout suffix); the
    thread gauge passes unit: '' and max: 100, so it shows the raw count
    (8, 150, …) while still acting as a thread-leak indicator (amber past 60,
    red past 80). The defaults (max: 100, unit: '%') keep every percentage
    gauge rendering exactly as before.

Full Changelog: v26.06.105...v26.06.106

v26.06.105

Choose a tag to compare

@github-actions github-actions released this 15 Jun 18:39
9e5ddd2

v26.06.105 (2026-06-15)

Fixed

  • Management-port observability now covers BOTH the application and the
    management ports.
    When actuator + admin run on a separate
    pyfly.management.server.port, the dashboard/actuator there is the single
    observability pane — but it previously reflected only the application port:
    • Access log: the management app was built without RequestLoggingFilter,
      so health probes, Prometheus scrapes and admin calls to the management port
      produced no http_request log line. It is now wired into the management
      chain (honoring pyfly.web.request-logging.enabled), so management-port
      traffic is logged through pyfly's structured logger like the main app.
    • Metrics / HTTP exchanges / traces: the data-capture filters
      (MetricsFilter, HttpExchangeRecorderFilter, the admin TraceCollector)
      ran only on the main app, so http.server.requests, /actuator/httpexchanges
      and the admin Traces view excluded management-port traffic. The same shared
      capture instances
      now also run on the management app, so the dashboard
      reflects both ports. A request traverses exactly one app's chain, so there is
      no double counting; the recorders keep their own path exclusions (Prometheus
      scrape, admin SSE self-polling).

Full Changelog: v26.06.104...v26.06.105