diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2157b91..8ee3bd3e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
---
+## v26.10.06 (2026-10-14)
+
+### Fixed
+
+- Honor standard OpenTelemetry service identity for newly created trace and
+ metric providers ([#151](https://github.com/fireflyframework/fireflyframework-pyfly/issues/151)).
+ An explicit nonempty `pyfly.observability.tracing.service-name`, including
+ `PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME`, retains priority; otherwise use
+ `OTEL_SERVICE_NAME`, then `service.name` from `OTEL_RESOURCE_ATTRIBUTES`,
+ then `pyfly.app.name`, then `pyfly-app`.
+- Preserve other SDK resource attributes and use SDK parsing for resource
+ environment values, including URL encoding. Remove the implicit framework
+ service-name default that previously masked standard environment identity.
+
+### Documentation
+
+- Explain identity precedence and deliberate overrides in the observability
+ guide and both book editions; examples leave the service name unset so a
+ platform-injected `OTEL_SERVICE_NAME` is respected.
+- Synchronize package, installer, site, and book download examples with
+ `v26.10.06` (normalized package version `26.10.6`).
+
+### Compatibility
+
+- Existing global and user-defined providers keep their resources. Identity is
+ configured at provider creation; restart the process to apply changes to
+ process-global providers. This release does not redeploy running workloads
+ or rewrite historical telemetry.
+
## v26.10.05 (2026-10-14)
### Changed
diff --git a/README.md b/README.md
index 13ca3940..42d84e54 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
-
+
@@ -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.05:** **[English PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-by-example.pdf)** · **[English EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-by-example.epub)** · **[Spanish PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-by-example-es.pdf)** · **[Spanish EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/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.06:** **[English PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-by-example.pdf)** · **[English EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-by-example.epub)** · **[Spanish PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-by-example-es.pdf)** · **[Spanish EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/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`.
---
@@ -850,21 +850,23 @@ 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.05:** see the [dependency update notes](docs/dependency-updates.md),
-including the temporary upstream source requirement and required pip upgrade
-step for the optional PII extra.
+**Upgrading to v26.10.06:** see [OpenTelemetry service identity](docs/modules/observability.md#service-identity-and-resource-attributes)
+for standard environment-variable support in traces and metrics. The
+[dependency update notes](docs/dependency-updates.md) still apply, including the
+temporary upstream source requirement and required pip upgrade step for the
+optional PII extra.
### Install from GitHub Release (Recommended)
```bash
# Install the latest release (uv)
-uv add "pyfly @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.5-py3-none-any.whl"
+uv add "pyfly @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.6-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.5-py3-none-any.whl"
+uv add "pyfly[web,data-relational,cache] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.6-py3-none-any.whl"
# Or with pip
-pip install "pyfly @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.5-py3-none-any.whl"
+pip install "pyfly @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/latest/download/pyfly-26.10.6-py3-none-any.whl"
```
### One-Line Install (CLI + Framework)
@@ -1194,6 +1196,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.06`** (2026-10-14) — OpenTelemetry service identity for newly created trace and metric providers honors standard resource environment variables while preserving explicit PyFly overrides.
- **`v26.10.05`** (2026-10-14) — refreshed dependencies and pinned Python setup workflows, with synchronized release metadata and English/Spanish book downloads.
- **`v26.10.04`** (2026-10-14) — revised public OAuth references and rebuilt English/Spanish PDF/EPUB books, with synchronized package and installer metadata.
- **`v26.10.03`** (2026-10-14) — authorization-code/PKCE redemption through the native HTTP endpoint, with form-encoded OAuth Basic credentials across acquisition and introspection.
diff --git a/book/README.md b/book/README.md
index 79e14e60..bec0af8b 100644
--- a/book/README.md
+++ b/book/README.md
@@ -7,10 +7,10 @@ wallet offer route, shared targeting fixture, runtime gates and operator workflo
| Edition | PDF | EPUB |
|---|---|---|
-| English | [PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-by-example.pdf) | [EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-by-example.epub) |
-| Spanish | [PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-by-example-es.pdf) | [EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-by-example-es.epub) |
+| English | [PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-by-example.pdf) | [EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-by-example.epub) |
+| Spanish | [PDF](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-by-example-es.pdf) | [EPUB](https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-by-example-es.epub) |
-These books are rebuilt from the `v26.10.05` software tag. The independently
+These books are rebuilt from the `v26.10.06` software tag. The independently
published [2026-10-14 edition](https://github.com/fireflyframework/fireflyframework-pyfly/releases/tag/books-26.10.14)
retains its original assets. For other editions, open the
[tagged releases](https://github.com/fireflyframework/fireflyframework-pyfly/releases)
diff --git a/book/manuscript-es/15-observability.md b/book/manuscript-es/15-observability.md
index ab2762cc..f79c34c9 100644
--- a/book/manuscript-es/15-observability.md
+++ b/book/manuscript-es/15-observability.md
@@ -192,11 +192,11 @@ Esa pasada de redacción se ejecuta para *cada* logger del proceso, incluidos lo
El motor de expresiones regulares viene con cada instalación. El motor NER respaldado por Presidio —que además atrapa nombres y direcciones en texto libre— está disponible mediante el extra `[pii]` y se activa automáticamente cuando se instala:
```bash
-uv add "pyfly[observability,pii] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-26.10.5-py3-none-any.whl"
+uv add "pyfly[observability,pii] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-26.10.6-py3-none-any.whl"
uv run python -m spacy download en_core_web_sm
```
-En v26.10.05, el extra PII requiere Git y acceso a GitHub: fija una corrección
+En v26.10.06, el extra PII requiere Git y acceso a GitHub: fija una corrección
oficial del código fuente de Presidio para poder instalar cryptography 50.
Ese código conserva el mismo número de versión que el paquete anterior de PyPI.
Las instalaciones existentes con pip deben reinstalar explícitamente el
@@ -687,12 +687,39 @@ pyfly:
observability:
tracing:
enabled: true
- service-name: "${pyfly.app.name}"
exporter: otlp
otlp:
endpoint: "http://localhost:4318"
```
+Deja `tracing.service-name` sin definir para respetar la identidad que inyecta
+la plataforma. Al crear proveedores de trazas y métricas, PyFly resuelve
+`service.name` primero desde un `pyfly.observability.tracing.service-name` no
+vacío (incluido `PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME`), después desde
+`OTEL_SERVICE_NAME`, después desde `service.name` en
+`OTEL_RESOURCE_ATTRIBUTES`, después desde `pyfly.app.name` y, por último,
+desde `pyfly-app`.
+
+```bash
+export OTEL_SERVICE_NAME=lumen-api
+export OTEL_RESOURCE_ATTRIBUTES='service.namespace=lumen,release.note=blue%2Cgreen'
+```
+
+El SDK decodifica los valores de los atributos (`blue%2Cgreen` pasa a ser
+`blue,green`) y PyFly conserva los demás atributos del recurso. Para sustituir
+deliberadamente la identidad del despliegue, define
+`tracing.service-name: lumen-custom` dentro de `pyfly.observability`, o usa
+`PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME`. Definir explícitamente
+`service-name: "${pyfly.app.name}"` también tiene prioridad sobre el entorno
+OpenTelemetry; no hace falta para usar el nombre de la aplicación como respaldo.
+
+La identidad se configura al crear el proveedor. Reinicia el proceso para
+cambiar los recursos globales; los proveedores globales existentes y los
+personalizados se reutilizan sin cambiar sus nombres. Esta versión no
+redespliega aplicaciones en ejecución ni reescribe la telemetría histórica.
+Consulta la [guía de identidad del servicio](https://fireflyframework.github.io/fireflyframework-pyfly/docs/modules/observability/#service-identity-and-resource-attributes)
+para conocer el contrato completo de propiedad de los proveedores.
+
**Ejecútalo — observa spans sin un colector.** Levantar Jaeger o Tempo es
excesivo mientras estás aprendiendo. Configura en su lugar el **exportador de
consola**, que imprime cada span por stdout. Lumen viene con
diff --git a/book/manuscript/15-observability.md b/book/manuscript/15-observability.md
index 77caf834..c37aa3dc 100644
--- a/book/manuscript/15-observability.md
+++ b/book/manuscript/15-observability.md
@@ -188,11 +188,11 @@ That redaction pass runs for *every* logger in the process, including ones insid
The regex engine ships with every install. The Presidio-backed NER engine — which also catches free-text names and addresses — is available via the `[pii]` extra and activates automatically when installed:
```bash
-uv add "pyfly[observability,pii] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-26.10.5-py3-none-any.whl"
+uv add "pyfly[observability,pii] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-26.10.6-py3-none-any.whl"
uv run python -m spacy download en_core_web_sm
```
-For v26.10.05, the optional PII extra requires Git and GitHub access: it pins
+For v26.10.06, the optional PII extra requires Git and GitHub access: it pins
an official Presidio source fix so cryptography 50 can be installed. The source
still reports the same Presidio version as its older PyPI package. Existing pip
installations must explicitly reinstall the pinned anonymizer before upgrading;
@@ -743,12 +743,36 @@ pyfly:
observability:
tracing:
enabled: true
- service-name: "${pyfly.app.name}"
exporter: otlp
otlp:
endpoint: "http://localhost:4318"
```
+Leave `tracing.service-name` unset to honor an identity injected by the platform.
+For newly created trace and metric providers, PyFly resolves `service.name` from
+a nonempty `pyfly.observability.tracing.service-name` (including
+`PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME`) first, then `OTEL_SERVICE_NAME`,
+then `service.name` in `OTEL_RESOURCE_ATTRIBUTES`, then `pyfly.app.name`, and
+finally `pyfly-app`.
+
+```bash
+export OTEL_SERVICE_NAME=lumen-api
+export OTEL_RESOURCE_ATTRIBUTES='service.namespace=lumen,release.note=blue%2Cgreen'
+```
+
+The SDK decodes resource attribute values (`blue%2Cgreen` becomes `blue,green`)
+and PyFly preserves the other resource attributes. To override deployment
+identity deliberately, set `tracing.service-name: lumen-custom` under
+`pyfly.observability`, or use `PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME`.
+An explicit `service-name: "${pyfly.app.name}"` also overrides the standard
+OpenTelemetry environment; it is not needed for the app-name fallback.
+
+Identity is configured at provider creation. Restart the process to change
+process-global resources; existing globals and custom providers are reused
+without renaming. This release does not redeploy running applications or rewrite
+historical telemetry. See the [service identity guide](https://fireflyframework.github.io/fireflyframework-pyfly/docs/modules/observability/#service-identity-and-resource-attributes)
+for the full provider ownership contract.
+
**Run it — see spans without a collector.** Standing up Jaeger or Tempo is
overkill while you are learning. Set the **console exporter** instead, which
prints each span to stdout. Lumen ships with `tracing.enabled: false`; flip it on
diff --git a/docs/cli.md b/docs/cli.md
index 109887f9..b3b80bcf 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -776,7 +776,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.05
+✓ pyfly v26.10.06
```
### Summary
diff --git a/docs/dependency-updates.md b/docs/dependency-updates.md
index fb1bea2c..60a20129 100644
--- a/docs/dependency-updates.md
+++ b/docs/dependency-updates.md
@@ -31,11 +31,11 @@ a release that supports cryptography 50 and that release is qualified.
For an existing pip installation, first reinstall the named anonymizer from the
exact source reference. Pip can otherwise retain the old index package because
-both sources report version `2.2.364`, even with `--upgrade`. Then upgrade PyFly:
+both sources report version `2.2.364`, even with `--upgrade`. Then upgrade PyFly to v26.10.06, which retains this source requirement:
```bash
python -m pip install --upgrade --force-reinstall "presidio-anonymizer @ git+https://github.com/data-privacy-stack/presidio.git@5e2fcea990aa3b99660d2aea9121d0ba74a8940b#subdirectory=presidio-anonymizer"
-python -m pip install --upgrade "pyfly[pii,security] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-26.10.5-py3-none-any.whl"
+python -m pip install --upgrade "pyfly[pii,security] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-26.10.6-py3-none-any.whl"
python -m pip check
python -m spacy download en_core_web_sm
```
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 7f030cea..2568d190 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -480,7 +480,7 @@ ______ ___.__._/ ____\ | ___.__.
| __// ____| |__| |____/ ____|
|__| \/ \/
- PyFly v26.10.05 | Python 3.12.0
+ PyFly v26.10.06 | 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
diff --git a/docs/installation.md b/docs/installation.md
index 13dae787..94aa36cf 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -323,7 +323,7 @@ Each extra pulls in the third-party libraries needed for a specific framework mo
| `cli` | click, rich, jinja2, questionary | CLI tools (pyfly new, run, info, doctor, db) |
| `full` | Bundled application extras (excludes `pii`, `testing`, `testcontainers`, and `argon2`) | Broad framework installation; add optional PII/testing/password backends explicitly |
-The optional `pii` extra in v26.10.05 requires Git and GitHub access for the
+The optional `pii` extra in v26.10.06 requires Git and GitHub access for the
immutable upstream anonymizer fix. See the [dependency update notes](dependency-updates.md#presidio-and-cryptography)
before upgrading an existing PII installation.
@@ -517,7 +517,7 @@ PyFly Doctor
✓ mypy — Type checker
PyFly packages:
- ✓ pyfly v26.10.05
+ ✓ pyfly v26.10.06
All checks passed!
```
diff --git a/docs/modules/configuration.md b/docs/modules/configuration.md
index f8c09bf6..38952028 100644
--- a/docs/modules/configuration.md
+++ b/docs/modules/configuration.md
@@ -1315,7 +1315,10 @@ read-only deployment. A separate management listener additionally requires
|---|---|---|
| `pyfly.observability.metrics.enabled` | `true` | Enable metrics collection. |
| `pyfly.observability.tracing.enabled` | `true` | Enable distributed tracing. |
-| `pyfly.observability.tracing.service-name` | `"${pyfly.app.name}"` | OpenTelemetry service name (defaults to app name). |
+| `pyfly.observability.tracing.service-name` | unset | Explicit nonempty override for newly created trace and metric resources; otherwise `OTEL_SERVICE_NAME`, resource environment `service.name`, app name, then `pyfly-app`. |
+
+See [service identity and resource attributes](observability.md#service-identity-and-resource-attributes)
+for standard environment variables, preserved attributes, and provider restart behavior.
### Full YAML Reference
@@ -1441,7 +1444,6 @@ pyfly:
enabled: true
tracing:
enabled: true
- service-name: "${pyfly.app.name}"
transactional:
enabled: false
saga:
diff --git a/docs/modules/core.md b/docs/modules/core.md
index 105e7c49..144d23fe 100644
--- a/docs/modules/core.md
+++ b/docs/modules/core.md
@@ -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.05)` |
+| `MINIMAL` | Single line: `:: PyFly :: (v26.10.06)` |
| `OFF` | No banner output at all. |
### BannerPrinter Class
@@ -640,7 +640,7 @@ ______ ___.__._/ ____\ | ___.__.
| __// ____| |__| |____/ ____|
|__| \/ \/
-:: PyFly Framework :: (v26.10.05)
+:: PyFly Framework :: (v26.10.06)
```
### Custom Banner Files
diff --git a/docs/modules/logging.md b/docs/modules/logging.md
index 788d8bb5..91f10165 100644
--- a/docs/modules/logging.md
+++ b/docs/modules/logging.md
@@ -252,10 +252,10 @@ country code.
Install the optional extra from the release artifact:
```bash
-pip install "pyfly[pii] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.05/pyfly-26.10.5-py3-none-any.whl"
+pip install "pyfly[pii] @ https://github.com/fireflyframework/fireflyframework-pyfly/releases/download/v26.10.06/pyfly-26.10.6-py3-none-any.whl"
```
-Version 26.10.05 pins the official Presidio anonymizer source fix for
+Version 26.10.06 pins the official Presidio anonymizer source fix for
cryptography 50. This extra requires Git and GitHub access during installation;
existing pip installations must explicitly reinstall the pinned anonymizer
source before upgrading. See [dependency update notes](../dependency-updates.md#presidio-and-cryptography)
diff --git a/docs/modules/observability.md b/docs/modules/observability.md
index 8e3c7f99..8e8804d9 100644
--- a/docs/modules/observability.md
+++ b/docs/modules/observability.md
@@ -943,7 +943,6 @@ pyfly:
observability:
tracing:
enabled: true # Default: true
- service-name: "${pyfly.app.name}" # Inherits app name by default
exporter: otlp # otlp | console | none
otlp:
endpoint: "http://localhost:4318" # OTLP/HTTP exporter endpoint
@@ -976,6 +975,46 @@ configured under `metrics.otlp.endpoint` or `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
traces endpoint with `/v1/traces` rewritten to `/v1/metrics` (a base URL gains `/v1/metrics`).
With no endpoint at all the provider has no reader: instruments still work, nothing is exported.
+### Service identity and resource attributes
+
+For each newly created OpenTelemetry trace or metric provider, PyFly chooses
+the resource's `service.name` in this order:
+
+1. A nonempty `pyfly.observability.tracing.service-name`, including an explicit
+ `PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME` environment override.
+2. `OTEL_SERVICE_NAME`.
+3. `service.name` from `OTEL_RESOURCE_ATTRIBUTES`.
+4. `pyfly.app.name`.
+5. `pyfly-app`.
+
+Leave `tracing.service-name` unset when your deployment supplies the standard
+OpenTelemetry identity. The framework default does not set this property:
+adding `service-name: "${pyfly.app.name}"` yourself is an explicit override
+that takes precedence over both OpenTelemetry environment variables.
+
+```bash
+export OTEL_SERVICE_NAME=lumen-api
+export OTEL_RESOURCE_ATTRIBUTES='service.namespace=lumen,deployment.environment.name=production,release.note=blue%2Cgreen'
+uv run pyfly run --server uvicorn
+```
+
+The OpenTelemetry SDK parses resource environment attributes, including
+URL-encoded values: `release.note` above becomes `blue,green`. Other resource
+attributes, including SDK metadata, are preserved when PyFly selects the service
+name. Both newly created providers use the same resolution rules; this does not
+change Prometheus metric names or labels.
+
+To deliberately override a platform-injected identity for both signals, set
+`pyfly.observability.tracing.service-name: lumen-custom` in your configuration,
+or `PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME=lumen-custom` in the environment.
+An empty property lets resolution continue to the OpenTelemetry environment.
+
+Resource identity is fixed when the provider is created. Restart the process
+after changing these settings for process-global providers. PyFly reuses
+existing globals and backs off for user-defined providers; it does not rename
+their resources. This change does not redeploy workloads or rewrite historical
+traces or metrics in your telemetry backend.
+
### Provider ownership and isolated contexts
Default providers are **process owned**: OpenTelemetry's SDK shuts them down at process exit.
diff --git a/docs/versioning.md b/docs/versioning.md
index 43471fac..f70676ae 100644
--- a/docs/versioning.md
+++ b/docs/versioning.md
@@ -64,6 +64,7 @@ rare case where a substantial change needs an additional review window.
| Version | Date | Notes |
|---------|------|-------|
+| `26.10.06` | 2026-10-14 | OpenTelemetry resource environment variables identify newly created trace and metric providers; explicit PyFly service-name overrides retain precedence. |
| `26.10.05` | 2026-10-14 | Refreshed dependencies, pinned Python setup workflows, and synchronized release metadata and book downloads. |
| `26.10.04` | 2026-10-14 | Revised OAuth references and EN/ES books included in the software release; synchronized release metadata. |
| `26.10.03` | 2026-10-14 | HTTP authorization-code/PKCE redemption and interoperable OAuth Basic credentials. |
@@ -103,20 +104,20 @@ shipped, with the version metadata updated.
```python
import pyfly
-print(pyfly.__version__) # → "26.10.05"
+print(pyfly.__version__) # → "26.10.06"
```
The CLI reads the installed distribution metadata and reports the normalized
package version:
```bash
-pyfly --version # → pyfly, version 26.10.5
+pyfly --version # → pyfly, version 26.10.6
```
The startup banner displays the leading-zero form:
```
-:: PyFly Framework :: (v26.10.05) (Python 3.13.9)
+:: PyFly Framework :: (v26.10.06) (Python 3.13.9)
```
---
diff --git a/install.sh b/install.sh
index 83258cff..7a7ba742 100755
--- a/install.sh
+++ b/install.sh
@@ -27,7 +27,7 @@ set -euo pipefail
# ── Constants ──────────────────────────────────────────────────────────────────
-PYFLY_VERSION="26.10.05"
+PYFLY_VERSION="26.10.06"
PYFLY_REPO="https://github.com/fireflyframework/fireflyframework-pyfly.git"
DEFAULT_INSTALL_DIR="$HOME/.pyfly"
MIN_PYTHON_MAJOR=3
diff --git a/pyproject.toml b/pyproject.toml
index eb87ace4..3b72fc3a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -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.5"
+version = "26.10.6"
description = "The official Python implementation of the Firefly Framework — DI, CQRS, EDA, hexagonal architecture, and more."
readme = "README.md"
license = "Apache-2.0"
diff --git a/src/pyfly/__init__.py b/src/pyfly/__init__.py
index cd804978..a628ea4b 100644
--- a/src/pyfly/__init__.py
+++ b/src/pyfly/__init__.py
@@ -14,4 +14,4 @@
# limitations under the License.
"""PyFly — Enterprise Python Framework."""
-__version__ = "26.10.05"
+__version__ = "26.10.06"
diff --git a/src/pyfly/observability/auto_configuration.py b/src/pyfly/observability/auto_configuration.py
index e806caec..6b3f66c7 100644
--- a/src/pyfly/observability/auto_configuration.py
+++ b/src/pyfly/observability/auto_configuration.py
@@ -83,17 +83,18 @@ async def close(self) -> None:
@staticmethod
def _service_name(config: Config) -> str:
- """``pyfly.observability.tracing.service-name``, else ``pyfly.app.name``, else ``pyfly-app``.
+ """Explicit PyFly override, then standard OTel environment, then app name.
Shared with :class:`MeterProviderAutoConfiguration` so traces and metrics carry one
``service.name`` — a dashboard joins the two on it.
"""
- return str(
- config.get(
- "pyfly.observability.tracing.service-name",
- config.get("pyfly.app.name", "pyfly-app"),
- )
- )
+ from opentelemetry.sdk.resources import OTELResourceDetector
+
+ explicit = config.get("pyfly.observability.tracing.service-name")
+ if explicit:
+ return str(explicit)
+ detected = OTELResourceDetector().detect().attributes.get("service.name")
+ return str(detected or config.get("pyfly.app.name") or "pyfly-app")
@bean
def tracer_provider(self, config: Config) -> TracerProvider:
diff --git a/src/pyfly/resources/pyfly-defaults.yaml b/src/pyfly/resources/pyfly-defaults.yaml
index e7ef0d28..9e9afc3e 100644
--- a/src/pyfly/resources/pyfly-defaults.yaml
+++ b/src/pyfly/resources/pyfly-defaults.yaml
@@ -209,7 +209,7 @@ pyfly:
enabled: true
tracing:
enabled: true
- service-name: "${pyfly.app.name}"
+ # Leave service-name unset so the platform's OTEL resource identity can take precedence.
transactional:
enabled: false
saga:
diff --git a/tests/observability/test_otel_resource_identity.py b/tests/observability/test_otel_resource_identity.py
new file mode 100644
index 00000000..a5a1288b
--- /dev/null
+++ b/tests/observability/test_otel_resource_identity.py
@@ -0,0 +1,152 @@
+"""Issue #151: exported traces and metrics retain the platform's service identity."""
+
+import json
+import os
+import queue
+import threading
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+from pathlib import Path
+
+import pytest
+from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ExportMetricsServiceRequest
+from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import ExportTraceServiceRequest
+
+from pyfly.core.config import Config
+from pyfly.observability.auto_configuration import MeterProviderAutoConfiguration, TracingAutoConfiguration
+
+
+@pytest.fixture
+def otlp_receiver(monkeypatch):
+ for name in list(os.environ):
+ if name.startswith("OTEL_") or name in {
+ "PYFLY_APP_NAME",
+ "PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME",
+ "PYFLY_OBSERVABILITY_TRACING_EXPORTER",
+ "PYFLY_OBSERVABILITY_TRACING_OTLP_ENDPOINT",
+ "PYFLY_OBSERVABILITY_METRICS_OTLP_ENDPOINT",
+ "PYFLY_OBSERVABILITY_TRACING_REGISTER_GLOBAL",
+ "PYFLY_OBSERVABILITY_METRICS_REGISTER_GLOBAL",
+ }:
+ monkeypatch.delenv(name)
+ received = queue.Queue()
+
+ class Receiver(BaseHTTPRequestHandler):
+ def do_POST(self):
+ received.put((self.path, self.rfile.read(int(self.headers["Content-Length"]))))
+ self.send_response(200)
+ self.send_header("Content-Length", "0")
+ self.end_headers()
+
+ def log_message(self, *args):
+ pass
+
+ server = ThreadingHTTPServer(("127.0.0.1", 0), Receiver)
+ thread = threading.Thread(target=lambda: server.serve_forever(poll_interval=0.01), daemon=True)
+ thread.start()
+ try:
+ yield f"http://127.0.0.1:{server.server_port}", received
+ finally:
+ server.shutdown()
+ server.server_close()
+ thread.join(timeout=2)
+
+
+@pytest.mark.parametrize(
+ "app_name,explicit_name,env,expected",
+ [
+ (None, None, {"OTEL_SERVICE_NAME": "core-flydocs"}, "core-flydocs"),
+ (
+ None,
+ None,
+ {"OTEL_RESOURCE_ATTRIBUTES": "service.name=core-flydocs-worker"},
+ "core-flydocs-worker",
+ ),
+ (
+ "shared-app",
+ None,
+ {"OTEL_SERVICE_NAME": "core-flydocs-bbox-worker", "OTEL_RESOURCE_ATTRIBUTES": "service.name=ignored"},
+ "core-flydocs-bbox-worker",
+ ),
+ (
+ None,
+ None,
+ {"OTEL_SERVICE_NAME": "", "OTEL_RESOURCE_ATTRIBUTES": "service.name=attributes-worker"},
+ "attributes-worker",
+ ),
+ ("app-fallback", None, {"OTEL_SERVICE_NAME": "", "OTEL_RESOURCE_ATTRIBUTES": ""}, "app-fallback"),
+ (None, None, {}, "pyfly-app"),
+ (
+ "shared-app",
+ "explicit-service",
+ {"OTEL_SERVICE_NAME": "injected-service", "OTEL_RESOURCE_ATTRIBUTES": "service.name=attributes-service"},
+ "explicit-service",
+ ),
+ (
+ None,
+ "yaml-service",
+ {"PYFLY_OBSERVABILITY_TRACING_SERVICE_NAME": "pyfly-env-service", "OTEL_SERVICE_NAME": "otel-service"},
+ "pyfly-env-service",
+ ),
+ ("shared-app", "", {"OTEL_SERVICE_NAME": "injected-service"}, "injected-service"),
+ (
+ None,
+ None,
+ {"OTEL_RESOURCE_ATTRIBUTES": "invalid,service.name=encoded%2Cworker%3Dblue"},
+ "encoded,worker=blue",
+ ),
+ ("app-fallback", None, {"OTEL_RESOURCE_ATTRIBUTES": "service.name="}, "app-fallback"),
+ ],
+ ids=[
+ "standard-service-env",
+ "resource-attributes",
+ "service-env-precedence",
+ "empty-env-uses-attributes",
+ "empty-env-uses-app",
+ "framework-fallback",
+ "explicit-pyfly-config",
+ "pyfly-env-override",
+ "empty-explicit-config",
+ "sdk-resource-parsing",
+ "empty-attribute-uses-app",
+ ],
+)
+def test_exported_signals_share_resolved_service_identity(
+ tmp_path: Path, monkeypatch, otlp_receiver, app_name, explicit_name, env, expected
+):
+ endpoint, received = otlp_receiver
+ for name, value in env.items():
+ monkeypatch.setenv(name, value)
+ resource_attributes = "service.namespace=platform,deployment.environment.name=test"
+ if env.get("OTEL_RESOURCE_ATTRIBUTES"):
+ resource_attributes += "," + env["OTEL_RESOURCE_ATTRIBUTES"]
+ monkeypatch.setenv("OTEL_RESOURCE_ATTRIBUTES", resource_attributes)
+ tracing = {"register-global": False, "otlp": {"endpoint": endpoint}}
+ if explicit_name is not None:
+ tracing["service-name"] = explicit_name
+ data = {"pyfly": {"observability": {"tracing": tracing, "metrics": {"register-global": False}}}}
+ if app_name is not None:
+ data["pyfly"]["app"] = {"name": app_name}
+ (tmp_path / "pyfly.yaml").write_text(json.dumps(data))
+ config = Config.from_sources(tmp_path)
+ tracer = TracingAutoConfiguration().tracer_provider(config)
+ meter = None
+ try:
+ meter = MeterProviderAutoConfiguration().meter_provider(config)
+ with tracer.get_tracer("issue-151").start_as_current_span("workload-operation"):
+ meter.get_meter("issue-151").create_counter("workload.operations").add(1)
+ assert tracer.force_flush(timeout_millis=5000)
+ assert meter.force_flush(timeout_millis=5000)
+ exports = dict(received.get(timeout=5) for _ in range(2))
+ traces = ExportTraceServiceRequest.FromString(exports["/v1/traces"])
+ metrics = ExportMetricsServiceRequest.FromString(exports["/v1/metrics"])
+ assert traces.resource_spans[0].scope_spans[0].spans[0].name == "workload-operation"
+ assert metrics.resource_metrics[0].scope_metrics[0].metrics[0].sum.data_points[0].as_int == 1
+ for resource in (traces.resource_spans[0].resource, metrics.resource_metrics[0].resource):
+ attributes = {item.key: item.value.string_value for item in resource.attributes}
+ assert attributes["service.name"] == expected
+ assert attributes["service.namespace"] == "platform"
+ assert attributes["deployment.environment.name"] == "test"
+ finally:
+ tracer.shutdown()
+ if meter is not None:
+ meter.shutdown()
diff --git a/uv.lock b/uv.lock
index 8b274586..342917e4 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2992,7 +2992,7 @@ wheels = [
[[package]]
name = "pyfly"
-version = "26.10.5"
+version = "26.10.6"
source = { editable = "." }
dependencies = [
{ name = "pydantic" },
diff --git a/web/index.html b/web/index.html
index 4a37a308..ca483254 100644
--- a/web/index.html
+++ b/web/index.html
@@ -90,7 +90,7 @@
The official, project-driven book. Across 18 chapters in five parts it builds Lumen — a real wallet & ledger microservice — from an empty folder into a secured, observable, event-driven service. Every listing is drawn from a running project. Spring developers get a parity callout at every turn.