Commit 59eceb61 authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

docs(fm): describe what FM develop 853bb20 ships

/internal answers only on port 8083 (404 on the EWBI port 8082). Partner
tokens must carry the federation-manager audience. A partner has one
agreement, edited in full by PATCH. Zone offers stay in step through
ZONES ADD/REMOVE callbacks, re-checked every 600 s by default. Inbound
Device Location, inbound QoD and POST /internal/federation/outbound are
built; inbound Device Status is not. A partner-hosted instance's ref is
the requested name, else the bare id.

OEG's C29 (FM labels these as not built) no longer applies and is
removed.

Co-Authored-By: default avatarClaude Opus 5.5 <noreply@anthropic.com>
parent 1be4d65f
Loading
Loading
Loading
Loading
+16 −13
Changes for fm/implementation-and-delivery.md: 16 added lines, 13 removed lines.
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ Settings are environment variables with the `FM_` prefix (`core/config.py`; a `.

| Setting | Default | Purpose |
|---|---|---|
| `FM_PORT` | `8082` | EWBI and callback port |
| `FM_INTERNAL_PORT` | `8083` | The only port that answers `/internal/*`; must differ from `FM_PORT` |
| `FM_POSTGRES_URL` | `postgresql+asyncpg://fm:fm@localhost:5433/fm_db` | `fm_db` connection |
| `FM_POSTGRES_ECHO` | `false` | Log SQL statements |
| `FM_LOG_LEVEL` | `INFO` | Log level |
@@ -70,14 +72,14 @@ Settings are environment variables with the `FM_` prefix (`core/config.py`; a `.
| `FM_SRM_INTERNAL_URL` | `http://localhost:8081` | SRM internal HTTP base URL |
| `FM_KEYCLOAK_ISSUER` | `http://localhost:8090/realms/federation` | Issuer partners' tokens must carry |
| `FM_KEYCLOAK_JWKS_URL` | unset | Where FM fetches the signing keys when it can't reach the issuer URL; unset means the issuer's JWKS |
| `FM_KEYCLOAK_AUDIENCE` | `federation-manager` | Audience partner tokens must carry in `aud`. Not built yet |
| `FM_KEYCLOAK_AUDIENCE` | `federation-manager` | Audience partner tokens must carry in `aud`; a token without it is refused |
| `FM_FEDERATION_ID`, `FM_COUNTRY_CODE`, `FM_MCC`, `FM_MNCS` | `oop-local`, `ES`, `214`, `07` | Our OPG.04 identity, sent on `CreateFederation` and served on `GET /internal/identity` |
| `FM_PLATFORM_CAPS` | `serviceAPIs` | Platform capabilities we advertise |
| `FM_PARTNER_STATUS_LINK` | `http://127.0.0.1:8082/operatorplatform/federation/v1/callbacks/partner-status` | Callback URL handed to partners; a deployment sets the URL partners can reach |
| `FM_INSTANCE_STATUS_LINK` | `http://127.0.0.1:8082/operatorplatform/federation/v1/callbacks/instance-status` | Same, for `onInstanceStatusEvent` after our `InstallApp` |
| `FM_AVAIL_ZONE_NOTIF_LINK` | unset | `availZoneNotifLink` for zone notifications |
| `FM_ALLOW_INSECURE_PARTNER_ENDPOINTS` | `false` | Allow plain-HTTP partner endpoints; development only |
| `FM_ZONE_OFFER_SWEEP_SECONDS` | `600` | How often every federated partner's zone offer is re-checked, besides each agreement write and each `valid_from`/`valid_until` (REQ-FED-04). Not built yet |
| `FM_ZONE_OFFER_SWEEP_SECONDS` | `600` | How often every federated partner's zone offer is re-checked, besides each agreement write and each `valid_from`/`valid_until` (REQ-FED-04) |
| `FM_BOOTSTRAP_FEDERATION` | `false` | At startup, federate with every active partner that has no outbound context (ADR-0043) |
| `FM_PARTNER_SECRET_KEY` | unset | Key (base64url, 32 bytes) that encrypts typed-in partner secrets; unset, FM accepts only secret files (ADR-0067) |

@@ -101,7 +103,7 @@ Settings are environment variables with the `FM_` prefix (`core/config.py`; a `.
| Outbound deploy and onboarding | `application/outbound_deploy.py` | `OutboundDeploymentService` |
| SRM hand-off (ADR-0053) | `application/federated_handoff.py` | `FederatedDeployHandler`, `FederatedTerminateHandler` |
| Callbacks received (ADR-0054) | `application/callbacks.py` | `PartnerStatusCallbackService`, `InstanceStatusCallbackService` |
| Outbound routing (proposed) | `application/outbound.py`, `domain/routing.py` | `OutboundFederationService`, `RoutingResolver` |
| Outbound routing | `application/outbound.py`, `domain/routing.py` | `OutboundFederationService`, `RoutingResolver` |
| Topology hiding | `middleware/topology_hiding.py`, `domain/topology.py` | `TopologyHidingMiddleware`, `sanitize` |
| Persistence | `adapters/database/` | `PostgresPartnerRepo`, `PostgresTransactionRepo`, … |
| Outbound tokens and EWBI calls | `adapters/security/`, `adapters/http/` | `ClientSecretTokenProvider`, `HttpxEwbiClient` |
@@ -277,14 +279,14 @@ Not built yet: CI runs type checks, lint, format and pytest, but no OpenAPI cont
- **Security metadata and runtime:** Preserve the root OpenAPI OR between `oAuth2ClientCredentials: [fed-mgmt]` and `notifClientCredentials: [fed-mgmt-notif]`, plus callback-specific notification security. Runtime policy must implement the declared operation security exactly or record a deliberate profile deviation; a runtime scope check alone does not repair incorrect OpenAPI metadata.
- **SRM interface:** Validate that `command.srm.*` messages published by FM conform to `CommandEnvelopeV1` and the per-subject payload schemas defined in [srm/interface-contract.md](/srm/interface-contract.md).

#### S.3.1 Compliance Snapshot (2026-10-13, FM `develop` 8b79115)
#### S.3.1 Compliance Snapshot (FM `develop` 853bb20)

FM serves part of the OPG.04 surface on the canonical paths. Do not advertise OPG.04 v6.0 OpenAPI
conformance until the contract gate above exists and passes.

| Area | Status | Evidence / gap |
|---|---|---|
| Signed access-token JWT validation | Pass | RS256, JWKS key lookup, issuer/expiry, `fed-mgmt` (`fed-mgmt-notif` for callbacks), `azp` partner lookup |
| Signed access-token JWT validation | Pass | RS256, JWKS key lookup, issuer/expiry, audience `federation-manager`, `fed-mgmt` (`fed-mgmt-notif` for callbacks), `azp` partner lookup |
| Canonical methods and paths | Partial | Served: `CreateFederation`, `GetFederationDetails`, `DeleteFederationDetails`, `GetFederationHealth`, `GetZoneData`, `ZoneSubscribe`, `GetZoneDetails`, `ZoneUnsubscribe`, `InstallApp`, `RemoveApp`, `UploadFile`, `UploadArtefact`, `OnboardApplication`, `APIForwarding`, `GetServiceAPISessionInfo`, `RemoveServiceAPISession`, and the `partner-status` and `instance-status` callbacks. The other OPG.04 operations are not served |
| Federation health | Pass | `GET /{federationContextId}/health` |
| Error representation | Partial | `application/problem+json` with `type`, `title`, `status`, `detail`, `instance`; whether each operation's statuses match the contract is not checked |
@@ -340,14 +342,15 @@ Use FastAPI or WireMock plus a test Keycloak realm.

## U. Delivery Plan

**Status on FM `develop` (2026-10-13).** Foundation is done, except the capacity check (item 5). Dynamic
**Status on FM `develop` (853bb20).** Foundation is done, except the capacity check (item 5). Dynamic
Federation: done are the routing engine, the federation-management endpoints except `RenewFederation`
and `UpdateFederation`, the partner admin API for partners, the zone exchange on request, and the
topology-hiding tests; not built yet are rate limiting, the admin API for agreements and routing rules,
and catalogue re-sync; network capability dispatch is proposed. Outbound Federation: done are the
outbound token provider and EWBI client; the OEG → FM hand-off and the synchronous queries are proposed;
not built yet are usage records, the transaction query API, staging tests against a real partner, and
metrics. Advanced Federation: not started.
and `UpdateFederation`, the admin API for partners and agreements, the zone exchange on request with
`ZONES` `ADD`/`REMOVE` callbacks in both directions, inbound QoD dispatch, and the topology-hiding
tests; not built yet are rate limiting, the routing-rule admin API, and catalogue re-sync. Outbound
Federation: done are the outbound token provider, the EWBI client, FM's side of the OEG → FM hand-off
(`POST /internal/federation/outbound`) and the inbound Device Location query; OEG does not call the
hand-off yet, and inbound Device Status is not built; not built yet are usage records, the transaction
query API, staging tests against a real partner, and metrics. Advanced Federation: not started.

### Foundation

@@ -439,7 +442,7 @@ If a partner OP is down, do we fail the API request immediately (fast fail), att

### W.4 OEG → FM Hand-off Mechanism

The outbound federation hand-off (OEG detects non-local target, passes to FM) uses **internal HTTP from OEG to FM** for synchronous queries (device location/status); zone-based deployment goes through SRM instead (UC-FM-07). Proposed; to be discussed in the next releases. FM owns EWBI protocol details; OEG does not publish federation commands to the DataBus. See overview G3.
The outbound federation hand-off (OEG detects non-local target, passes to FM) uses **internal HTTP from OEG to FM** for synchronous queries (device location/status); zone-based deployment goes through SRM instead (UC-FM-07). FM serves `POST /internal/federation/outbound`; OEG does not call it yet. FM owns EWBI protocol details; OEG does not publish federation commands to the DataBus. See overview G3.

---

+12 −16
Changes for fm/overview-and-architecture.md: 12 added lines, 16 removed lines.
Original line number Diff line number Diff line
@@ -24,8 +24,8 @@ If you are new to FM, this is the simplest useful way to think about it:
- **The relay is byte-for-byte.** OEG forwards the raw GSMA payload and query string to FM's mirrored `/internal/*` endpoint over a synchronous internal HTTP call (RD §L.3b; ADR-0051). FM validates, executes and drives the outbound handshake.
- **The two directions use different paths.** They are not split at the ingress on a shared URL.
- **FM does not fulfill requests itself.** For OPG.04 lifecycle mutations (install/remove application and supported network-capability operations), it publishes the same `command.srm.*` commands to DataBus that OEG publishes (ADR-0001).
- **Stateless queries go to SRM over HTTP.** For device location and device status, FM calls SRM's internal HTTP endpoints synchronously. SRM handles all fulfillment the same way whether the request came from OEG or FM.
- **Federated Service API operations are proposed for a later release.** Federated Service API capabilities and queries are proposed; to be discussed in the next releases (UC-FM-04, UC-FM-05).
- **Stateless queries go to SRM over HTTP.** For device location, FM calls SRM's internal HTTP endpoint synchronously (device status is not built yet). SRM handles all fulfillment the same way whether the request came from OEG or FM.
- **Federated Service APIs cover Device Location and QoD.** A partner's Device Location query is answered through SRM, and a partner's QoD request becomes SRM network-capability commands (UC-FM-04, UC-FM-05). Device Status is not built yet.
- **FM enforces topology hiding.** Before any response leaves the operator boundary, FM strips internal IPs, cluster names, namespace names, pod UIDs, node names, and internal trace IDs. Partner operators never see our internal infrastructure details.
- **FM manages partner relationships.** It stores partner operator profiles, federation agreements, routing rules, and shared resource catalogues in its own PostgreSQL database (`fm_db`).
- **A new federation starts in one of two ways.** Either an operator calls one of the eight lifecycle operations on OEG, which relays it to FM byte-for-byte with no DataBus, no new schema and no reshaping (ADR-0051, keeping ADR-0046's transport, superseding ADR-0043's `command.fm.federation.*` transport). Or FM starts it itself from bootstrap configuration at startup (ADR-0043).
@@ -98,10 +98,10 @@ There are two success definitions:
| **Partner authentication** | OAuth2 Client-Credentials + signed JWT. Our Keycloak `federation` realm issues inbound tokens; each target partner's Authorization Server issues outbound tokens. FM validates `fed-mgmt` and maps inbound `azp` to the partner profile (ADR-0042). |
| **Federation agreement enforcement** | Check that the requesting partner has a valid agreement before routing any request. |
| **Outbound federation routing** | Resolve target partner from user identifiers (MSISDN — Mobile Station International Subscriber Directory Number — prefix, IP CIDR — Classless Inter-Domain Routing block) via routing table. Forward requests over EWBI to partner OPs. |
| **Inbound request dispatch** | Map OPG.04 `InstallApp`/`RemoveApp` and supported Service API operations to `command.srm.*` with `source=federation` and `federation_partner_ref` (ADR-0001). OPG.04 v1.4.0 defines no application-scale operation. Service API operations are proposed; to be discussed in the next releases. |
| **Inbound request dispatch** | Map OPG.04 `InstallApp`/`RemoveApp` and supported Service API operations to `command.srm.*` with `source=federation` and `federation_partner_ref` (ADR-0001). OPG.04 v1.4.0 defines no application-scale operation. The Service APIs built are Device Location and QoD. |
| **Operation tracking** | Consume `event.srm.operation.completed` from the DataBus and track the federation request lifecycle. Consuming `event.srm.operation.status` is not built yet. |
| **Topology hiding** | Strip all internal IPs, cluster names, namespace names, pod UIDs, node names, and internal trace IDs from responses before they cross the operator boundary. |
| **Resource catalogue sync** | Offer our zones to a partner on request; store the zones partners offer us and subscribe to them on the operator's request (UC-FM-03). Partner zone updates and re-sync are not built yet. |
| **Resource catalogue sync** | Offer our zones to a partner on request; store the zones partners offer us and subscribe to them on the operator's request (UC-FM-03). Both directions stay in step through `ZONES` `ADD`/`REMOVE` partner-status callbacks: FM sends them when an agreement change or a `valid_from`/`valid_until` alters our offer, re-checking every `FM_ZONE_OFFER_SWEEP_SECONDS` (600 s by default), and applies the ones partners send. Re-sync on `event.srm.catalog.updated` and the `zone-resource` callback are not built yet. |
| **Correlation ID management** | Keep internal `correlation_id` inside the platform; correlate with partners only through OPG.04 identifiers (`federationContextId`, `txnIdentifier`, `Idempotency-Key`). |

### A.2 EWBI OAuth2 Trust Flows
@@ -261,7 +261,7 @@ UC-FM-07 below — the hand-off there comes from SRM, not OEG.

A locally connected App Provider makes a request that targets a subscriber on a partner network. OEG detects that the target cannot be served locally and hands off to FM.

**Status:** proposed; to be discussed in the next releases.
**Status:** FM serves its side (`POST /internal/federation/outbound`); OEG does not call it yet.

- **Actor:** FM (on behalf of local App Provider, triggered by OEG)
- **Trigger:** OEG determines the request target (MSISDN, IP) belongs to a partner network.
@@ -339,7 +339,7 @@ sequenceDiagram

A partner operator requests a network capability (e.g., QoS session) for a subscriber that is on our network.

**Status:** proposed; to be discussed in the next releases.
**Status:** built for `QualityOnDemand`.

- **Actor:** Partner Operator
- **Trigger:** Partner OP calls OPG.04 `POST /operatorplatform/federation/v1/{federationContextId}/apiservice/{serviceAPINameVal}` for the relevant network Service API.
@@ -348,9 +348,8 @@ A partner operator requests a network capability (e.g., QoS session) for a subsc
  2. FM maps the code-owned `serviceAPINameVal` plus validated `ServiceAPIBody` schema to the corresponding normalized capability operation and DataBus subject; OPG.04 `APIForwarding` defines no generic activate/update/deactivate discriminator.
  3. FM writes a `federation_transactions` row (status=`pending`) before publish.
  4. FM publishes the command to DataBus with `source=federation`.
  5. SRM processes through the standard network capability pipeline.
  6. SRM publishes `event.srm.operation.completed`.
  7. FM topology-strips the result and returns to partner.
  5. FM answers the partner at once with the session id (hex) as `connectId`. Nothing waits on SRM.
  6. SRM processes the command through the standard network capability pipeline. The partner reads or removes the session with `GetServiceAPISessionInfo` and `RemoveServiceAPISession`; removal publishes `command.srm.network.capability.deactivate`.

```mermaid
sequenceDiagram
@@ -363,17 +362,14 @@ sequenceDiagram
    P->>F: APIForwarding capability request + JWT
    F->>F: Validate JWT and agreement
    F->>D: Store pending transaction
    F->>B: command.srm.network.capability.*
    F->>B: command.srm.network.capability.activate
    F-->>P: connectId
    B->>S: Deliver capability command
    S->>B: event.srm.operation.completed
    B->>F: Deliver completion event
    F->>D: Finalize transaction
    F-->>P: Sanitized capability result
```

### UC-FM-05: Inbound Federated Synchronous Query (Device Location / Device Status)

**Status:** proposed; to be discussed in the next releases.
**Status:** built for Device Location; Device Status is not built yet.

A partner operator queries device location or device status for a subscriber that is on our network. Unlike deployment or QoS requests, this follows a synchronous request-response path — no DataBus, no service orders. Audit is still recorded in `federation_transactions` with `operation_id` left null.

@@ -500,7 +496,7 @@ sequenceDiagram
- **EWBI termination** under `/operatorplatform/federation/v1/*` (partner-facing ingress) for all inbound partner operator requests — the entire prefix.
- **Operator-triggered federation lifecycle** for the outbound direction — the eight operations proxied by OEG from its own platform namespace as a synchronous internal HTTP call carrying the raw GSMA payload and query string byte-for-byte (§L.3b; ADR-0051), validated and executed by FM; on a different path from inbound entirely, not split at a shared ingress.
- **OAuth2 Client-Credentials authorization** with signed JWTs for all EWBI requests (`fed-mgmt`; `fed-mgmt-notif` for callbacks).
- **Federation agreement management** — partner profiles, agreements, routing rules, and catalogue sharing (the admin API for agreements and routing rules is not built yet).
- **Federation agreement management** — partner profiles, agreements, routing rules, and catalogue sharing (the admin API for routing rules is not built yet).
- **Inbound request dispatch** — publish `command.srm.*` commands to DataBus with `source=federation` for SRM fulfillment.
- **Charging usage integration** (later phase) — emit charging/usage records toward the operator-controlled charging system according to OPG.02 §4.3.3; inter-operator financial settlement remains outside FM's defined scope.
- **Outbound federation routing** — resolve target partner from routing identifiers, forward requests over EWBI.
+3 −3
Changes for fm/persistence-model.md: 3 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -212,7 +212,7 @@ Constraint: `UNIQUE (partner_op_id, direction, federation_context_id)`.
**Indexes:**
- `idx_federation_agreements_partner` on `partner_op_id`
- `idx_federation_agreements_status_validity` on `(status, valid_from, valid_until)`
- `uq_federation_agreements_one_per_partner`, unique on `partner_op_id` where `status <> 'expired'`: one live agreement per partner; older ones stay as `expired` history (not built yet)
- `uq_federation_agreements_one_per_partner`, unique on `partner_op_id` where `status <> 'expired'`: one live agreement per partner; older ones stay as `expired` history

#### `routing_rules`

@@ -354,9 +354,9 @@ What FM `develop` has today; update as the schema changes.

| Table | In code | Notes |
|---|---|---|
| `partner_ops` | Yes, except `supported_apis`, `sla_config`, `last_health_check_at` and `partner_ca_cert_path` (not built yet) | |
| `partner_ops` | Yes, except `sla_config`, `last_health_check_at` and `partner_ca_cert_path` (not built yet) | |
| `partner_op_secrets` | Yes | ADR-0067 |
| `federation_contexts` | Yes, except `offered_zone_ids` (not built yet) | |
| `federation_contexts` | Yes | |
| `federation_agreements` | Yes | `service_spec_mappings` is the `InstallApp` fallback after EWBI onboarding (ADR-0068) |
| `routing_rules` | Yes | |
| `shared_resource_catalogues` | Yes | Zones, zone subscriptions and application onboardings (ADR-0068) |
+18 −17

File changed.

Preview size limit exceeded, changes collapsed.

+0 −2
Changes for oeg/interface-contract.md: 0 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -798,8 +798,6 @@ API. `CreateFederation`'s identity fields must match FM's own
([fm/requirements-and-design.md §L.3b](/fm/requirements-and-design.md)), so the operator reads them
from `GET /identity` instead of typing them.

**Shared status disagreement C29:** FM design §L.2 still labels one-agreement enforcement/full term PATCH not built. The selected [FM agreement service](https://labs.etsi.org/rep/oop/code/federation-manager/-/blob/853bb20f77f63166918944ecde32b630e95d675b/src/federation_manager/application/agreements.py); [FM agreement PATCH schema](https://labs.etsi.org/rep/oop/code/federation-manager/-/blob/853bb20f77f63166918944ecde32b630e95d675b/src/federation_manager/api/internal/agreements.py); [FM agreement tests](https://labs.etsi.org/rep/oop/code/federation-manager/-/blob/853bb20f77f63166918944ecde32b630e95d675b/tests/test_internal_agreements.py); [FM agreement migration](https://labs.etsi.org/rep/oop/code/federation-manager/-/blob/853bb20f77f63166918944ecde32b630e95d675b/src/federation_manager/migrations/versions/0008_one_agreement_per_partner.py) implement/refuse a second POST with 409 and support full term updates. OEG owns only forwarding; these source references do not attest deployment. C29 identifies the shared implementation-status mismatch.

**Inbound and outbound paths.** FM terminates inbound EWBI on the canonical
`/operatorplatform/federation/v1/*` prefix (§1); OEG's operator-facing surface is a distinct,
OEG-owned path that only FM's `/internal/*` mirror ever sees on the other side, never exposed
Loading