Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

---

## v26.10.12 (2026-10-27)

### Added

- Typed SSE events with IDs, retry hints and comments, a bounded asynchronous
emitter, optional heartbeats and an asynchronous response-factory preflight.
A single pull-driven producer owns stream iteration and cleanup so native
agent context managers remain valid across yields and disconnects. The
existing finite emitter and synchronous response construction remain available.
- Optional native OAuth2 authorization-parameter and verified-authentication
hooks, a public ID-token validator factory, configurable login claim mappings
and strict OIDC mode. Reserved state, nonce, PKCE and client parameters remain
framework controlled; policy denial happens before session identity publication.
- Opt-in raw verified claim mappings preserve structured JSON attributes without
changing existing string-only attribute mappings.

### Fixed

- Forward an explicitly configured OpenAI embedding API base URL to the SDK
instead of silently selecting its ambient/default endpoint.
- Forward explicitly configured Ollama embedding bearer credentials to the selected HTTP endpoint.
- Bind configuration properties before eager factories resolve dependent services,
preserving explicit user factories and late registration.
- Include IANA time zone data so named zones remain available without an
operating-system time zone database.
- Redact client registration credentials from generated representations.

## v26.10.11 (2026-10-24)

### Changed
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<a href="https://github.com/fireflyframework"><img src="https://img.shields.io/badge/Firefly_Framework-official-ff6600?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0xMiAyQzYuNDggMiAyIDYuNDggMiAxMnM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTBTMTcuNTIgMiAxMiAyeiIvPjwvc3ZnPg==" alt="Firefly Framework"></a>
<a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.12%2B-blue?logo=python&logoColor=white" alt="Python 3.12+"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-green" alt="License: Apache 2.0"></a>
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-26.10.11-brightgreen" alt="Version: 26.10.11"></a>
<a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-26.10.12-brightgreen" alt="Version: 26.10.12"></a>
<a href="https://mypy-lang.org/"><img src="https://img.shields.io/badge/type--checked-mypy%20strict-blue?logo=python&logoColor=white" alt="Type Checked: mypy strict"></a>
<a href="https://docs.astral.sh/ruff/"><img src="https://img.shields.io/badge/code%20style-ruff-purple?logo=ruff&logoColor=white" alt="Code Style: Ruff"></a>
<a href="#philosophy"><img src="https://img.shields.io/badge/async-first-brightgreen" alt="Async First"></a>
Expand Down Expand Up @@ -61,7 +61,7 @@

It covers the whole stack: dependency injection, configuration & profiles, the web layer, the Spring-Data `Repository` (derived queries, pagination, specifications, projections), DDD aggregates & `Money`, CQRS, domain events & event sourcing, messaging, HTTP clients, sagas, caching & resilience, security, observability, testing, scheduling, and going to production. Spring developers get a **Spring parity** callout at every turn.

**📥 Download the books shipped with v26.10.11:** **[English PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.11/pyfly-by-example.pdf)** · **[English EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.11/pyfly-by-example.epub)** · **[Spanish PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.11/pyfly-by-example-es.pdf)** · **[Spanish EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.11/pyfly-by-example-es.epub)**. For a specific version, use its [tagged release](https://github.com/fireflyframework/fireflyframework-pyfly/releases), or build from source with `bash book/build/run.sh --out-dir book/release-output`. Run the companion sample with `cd samples/lumen && uv run --extra dev pytest` and `uv run pyfly run --server uvicorn`.
**📥 Download the books shipped with v26.10.12:** **[English PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.12/pyfly-by-example.pdf)** · **[English EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.12/pyfly-by-example.epub)** · **[Spanish PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.12/pyfly-by-example-es.pdf)** · **[Spanish EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.12/pyfly-by-example-es.epub)**. For a specific version, use its [tagged release](https://github.com/fireflyframework/fireflyframework-pyfly/releases), or build from source with `bash book/build/run.sh --out-dir book/release-output`. Run the companion sample with `cd samples/lumen && uv run --extra dev pytest` and `uv run pyfly run --server uvicorn`.

---

Expand Down Expand Up @@ -851,7 +851,7 @@ See **[`samples/lumen/`](samples/lumen/README.md)** for an end-to-end DDD micros

> **Note:** PyFly is distributed exclusively via [GitHub Releases](https://github.com/fireflyframework/fireflyframework-pyfly/releases). It is **not** published to PyPI.

**Upgrading to v26.10.11:** This maintenance release preserves application APIs and the SQLAlchemy 2.0.50 minimum while refreshing the repository lock and book toolchain. Resolve and test your application lock. See the [dependency update notes](docs/dependency-updates.md).
**Upgrading to v26.10.12:** Native SSE adds typed events, bounded live emission and cancellation-safe cleanup. Browser login supports verified public policy hooks, embedding adapters preserve explicit endpoints and credentials, and configuration properties bind before dependent factories. Existing finite SSE calls remain supported. Refresh your application lock and see the [web](docs/modules/web.md) and [security](docs/modules/security.md) guides.

**From v26.10.10:** Optional script execution now uses Monty 0.0.23 with an owned worker and fresh session per run. Existing Code Mode calls keep their direct tool syntax. Remove non-`None` `max_allocations` limits; use memory, recursion and time budgets instead. See the [execution guide](docs/modules/agentic/execution.md).

Expand All @@ -871,13 +871,13 @@ hotfix baseline and the migration from the removed Presidio backend.

```bash
# Install the latest release (uv)
uv add "pyfly @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.11-py3-none-any.whl"
uv add "pyfly @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.12-py3-none-any.whl"

# Install with specific extras
uv add "pyfly[web,data-relational,cache] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.11-py3-none-any.whl"
uv add "pyfly[web,data-relational,cache] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.12-py3-none-any.whl"

# Or with pip
pip install "pyfly @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.11-py3-none-any.whl"
pip install "pyfly @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.12-py3-none-any.whl"
```

### One-Line Install (CLI + Framework)
Expand Down Expand Up @@ -1208,6 +1208,7 @@ The git tag and human-readable display use the leading-zero form (`v26.05.01`);

The full release history lives in **[CHANGELOG.md](CHANGELOG.md)** ([Keep a Changelog](https://keepachangelog.com/) format). Recent highlights:

- **`v26.10.12`** (2026-10-27) — native bounded SSE lifecycle, public OIDC hooks, explicit embedding connections and reliable early configuration binding.
- **`v26.10.11`** (2026-10-24) — preserve supported dependency minimums, keep automated project updates in the lockfile, and refresh the book toolchain and both editions.
- **`v26.10.10`** (2026-10-24) — adapt optional sandbox execution to Monty 0.0.23, preserve direct sync/async Code Mode tools, and refresh the English/Spanish execution tutorials.
- **`v26.10.09`** (2026-10-24) — remove Presidio and the `pii` extra while keeping native pattern redaction enabled, refresh compatible dependencies, and reduce the admin logo size.
Expand Down
2 changes: 1 addition & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@ Missing optional tools are shown with a `-` dash indicator (dimmed), while missi
Verifies that PyFly itself is importable and displays the installed version:

```
✓ pyfly v26.10.11
✓ pyfly v26.10.12
```

### Summary
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ ______ ___.__._/ ____\ | ___.__.
| __// ____| |__| |____/ ____|
|__| \/ \/

PyFly v26.10.11 | Python 3.12.0
PyFly v26.10.12 | Python 3.12.0

2026-01-15T10:30:00Z [info] starting_application app=my-service version=0.1.0
2026-01-15T10:30:00Z [info] no_active_profiles message=No active profiles set, falling back to default
Expand Down
2 changes: 1 addition & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ PyFly Doctor
✓ mypy — Type checker

PyFly packages:
✓ pyfly v26.10.11
✓ pyfly v26.10.12

All checks passed!
```
Expand Down
4 changes: 2 additions & 2 deletions docs/modules/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ class BannerMode(enum.Enum):
| Mode | Behavior |
|---|---|
| `TEXT` | Full ASCII art banner (default) with a framework version line. |
| `MINIMAL` | Single line: `:: PyFly :: (v26.10.11)` |
| `MINIMAL` | Single line: `:: PyFly :: (v26.10.12)` |
| `OFF` | No banner output at all. |

### BannerPrinter Class
Expand Down Expand Up @@ -640,7 +640,7 @@ ______ ___.__._/ ____\ | ___.__.
| __// ____| |__| |____/ ____|
|__| \/ \/

:: PyFly Framework :: (v26.10.11)
:: PyFly Framework :: (v26.10.12)
```

### Custom Banner Files
Expand Down
35 changes: 35 additions & 0 deletions docs/modules/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,41 @@ cooperative cancellation rather than a hard wall-clock guarantee.

---

## Browser login extension points

The framework owns the authorization-code flow, PKCE, state, nonce, ID-token
signature/issuer/audience/authorized-party checks, session rotation and persistence.
Applications may register optional beans implementing these public ports from
`pyfly.security.oauth2`:

- `OAuth2AuthorizationRequestCustomizer.customize(request, registration)` is
asynchronous and returns additional provider parameters. Reserved protocol
fields cannot be overridden. Validate deployment-approved provider hints in
this hook; do not forward arbitrary query parameters.
- `OAuth2AuthenticationSuccessHandler.on_success(request, registration, context)`
is asynchronous and runs after identity verification, before rotation or
publishing a new session identity. It returns `None`; raising an application
security exception denies login without persisting that identity.
- `OidcTokenValidatorFactory.create(registration)` returns a native
`JWKSTokenValidator`, allowing a bounded application-owned JWKS transport.
The native login handler still checks nonce, issuer, client audience and `azp`.

Enable `pyfly.security.oauth2.login.require-oidc-id-token: true` for OIDC-only
browser identity. This requires an ID token, issuer, nonce and configured
validator/JWKS endpoint. Generic OAuth userinfo fallback remains the default
for existing clients. An optional `ClaimMappings` bean customizes verified
identity mapping; otherwise login configuration accepts `principal-claim-names`,
`authorities-claim-names`, `scope-claim-names`, `attribute-claims`,
`raw-attribute-claims` and `authority-prefix` beneath that same login prefix.
`success-uri` configures the post-login destination.

`ClaimMappings.raw_attribute_claims` is an explicit allowlist of verified JSON
attributes such as numeric `auth_time` or list-valued `amr`. These values are
deep-copied. Existing `attribute_claims` retains string-only behavior. Resource
servers support the equivalent `raw-attribute-claims` configuration. Keep both
allowlists narrow; token attributes are not a substitute for current application
authorization. Client registration representations redact `client_secret`.

## Secure-by-Default & Hardening

PyFly's security defaults are chosen to fail closed. The behaviours below are active without extra configuration; operators should understand them before deploying.
Expand Down
43 changes: 43 additions & 0 deletions docs/modules/web.md
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,49 @@ The function returns a fully-formed SSE event string terminated by a double newl

**Source:** `src/pyfly/web/sse/response.py`

### Typed live streams and lifecycle

`SseEvent[T]` carries `data`, `event`, `id`, `retry` and `comment`. IDs and
event names cannot contain CR, LF or NUL; retry values are nonnegative
integers. Omitted data permits a comment-only heartbeat. JSON objects are
payloads, not implicit event envelopes.

```python
from pyfly.web.sse import SseEvent
from pyfly.web.sse.adapters.starlette import make_sse_response

async def prepare_stream(request):
await authorize(request) # completes before response headers
async def events():
async with open_native_agent_stream() as stream:
async for item in stream:
yield SseEvent(data=item.payload, event=item.kind, id=item.cursor)
return events()

response = await make_sse_response(
prepare_stream(request), heartbeat_interval=15,
max_event_bytes=1_048_576, headers={"Cache-Control": "no-store"},
)
```

The factory overload awaits authorization before constructing the response.
For an existing async iterable, `make_sse_response` still returns a response
synchronously. Coroutine `@sse_mapping` handlers may return either a prepared
response or an async iterable. Native agent streams remain inside their public
async context for the entire stream lifetime.

One producer task owns iteration, serialization and closure. The consumer
requests one event at a time; heartbeats never cancel a pending source read.
Disconnect closes the source and releases native contexts. It does not cancel
a durable background run: persistent replay, cursor authorization and explicit
run cancellation belong to the application. A source's own unbounded buffers
are outside this adapter's backpressure guarantee.

`AsyncSseEmitter(capacity=256, max_event_bytes=1_048_576)` supports live producers
with awaited `send`. A full buffer blocks producers. `close` drains queued
events; `aclose` discards them and releases blocked senders. It has one consumer
and is transient; it is not a durable event log.

### SseEmitter

The `SseEmitter` class provides a queue-based interface for building SSE events. It is itself an async iterable, making it suitable for passing to a streaming response:
Expand Down
7 changes: 4 additions & 3 deletions docs/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ rare case where a substantial change needs an additional review window.

| Version | Date | Notes |
|---------|------|-------|
| `26.10.12` | 2026-10-27 | Typed bounded SSE lifecycle, native OIDC policy hooks, explicit embedding credentials/endpoints and early property binding. |
| `26.10.11` | 2026-10-24 | Preserve supported dependency floors, use lockfile-only project updates and refresh book tooling and both editions. |
| `26.10.10` | 2026-10-24 | Monty 0.0.23 worker-backed execution, preserved Code Mode API and updated EN/ES tutorials. |
| `26.10.09` | 2026-10-24 | Native regex redaction replaces Presidio; dependency hotfix and smaller admin branding. |
Expand Down Expand Up @@ -109,20 +110,20 @@ shipped, with the version metadata updated.

```python
import pyfly
print(pyfly.__version__) # → "26.10.11"
print(pyfly.__version__) # → "26.10.12"
```

The CLI reads the installed distribution metadata and reports the normalized
package version:

```bash
pyfly --version # → pyfly, version 26.10.11
pyfly --version # → pyfly, version 26.10.12
```

The startup banner displays the leading-zero form:

```
:: PyFly Framework :: (v26.10.11) (Python 3.13.9)
:: PyFly Framework :: (v26.10.12) (Python 3.13.9)
```

---
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ set -euo pipefail

# ── Constants ──────────────────────────────────────────────────────────────────

PYFLY_VERSION="26.10.11"
PYFLY_VERSION="26.10.12"
PYFLY_REPO="https://github.com/fireflyframework/fireflyframework-pyfly.git"
DEFAULT_INSTALL_DIR="$HOME/.pyfly"
MIN_PYTHON_MAJOR=3
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "pyfly"
# CalVer YY.MM.PATCH — package metadata uses PEP 440 normalized form (26.5.4);
# git tag, GitHub release and human-readable display use leading-zero form
# (v26.05.04) to match the Java/.NET/Go siblings.
version = "26.10.11"
version = "26.10.12"
description = "The official Python implementation of the Firefly Framework — DI, CQRS, EDA, hexagonal architecture, and more."
readme = "README.md"
license = "Apache-2.0"
Expand All @@ -28,6 +28,7 @@ classifiers = [
dependencies = [
"pydantic>=2.14.1",
"pyyaml>=6.0.3",
"tzdata>=2025.2",
]

[project.urls]
Expand Down
13 changes: 12 additions & 1 deletion samples/agentic-book/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading