Commit 79bb4b0d authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

Merge branch 'fix/outbound-stores-context-id' into 'oeg-fm'

Fix/outbound stores context id

See merge request !6
parents 13adbf7d 1eb1b88f
Loading
Loading
Loading
Loading
+2 −2
Changes for decisions/0046-oeg-fronts-federation-lifecycle-ewbi-as-sync-proxy.md: 2 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -59,8 +59,8 @@ existing federation stay on FM's L.2 internal admin API directly (REQ-FED-19)
move them. The request body is the operator-facing shape FM's L.2 internal admin API already
defines (`POST /internal/partners` / `/internal/agreements`) — **not** a raw OPG.04
`FederationRequestData`. OEG performs no GSMA/OPG.04 mapping or protocol logic; FM alone maps the
operator request to the outbound OPG.04 `CreateFederation` / `ZoneSubscribe` handshake, mints
`federationContextId`, and returns it on the same synchronous response, which OEG relays to the
operator request to the outbound OPG.04 `CreateFederation` / `ZoneSubscribe` handshake, stores the
partner-issued `federationContextId`, and returns it on the same synchronous response, which OEG relays to the
operator.

**Inbound EWBI termination is unchanged.** Partner OPs calling `/operatorplatform/federation/v1/*`
+4 −4
Changes for fm/overview-and-architecture.md: 4 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ If you are new to FM, this is the simplest useful way to think about it:
- **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). For stateless queries (device location, device status), it calls SRM's internal HTTP endpoints synchronously. Either way, SRM handles all fulfillment identically regardless of whether the request came from OEG or FM.
- **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`).
- **Establishing a new federation is triggered two ways:** an operator-initiated call to one of the eight federation-lifecycle operations OEG serves for the outbound direction on its own platform namespace, which OEG proxies to FM over a synchronous internal HTTP request carrying the raw GSMA payload and query string byte-for-byte (no DataBus, no new schema, no reshaping; ADR-0051, keeping ADR-0046's transport, superseding ADR-0043's `command.fm.federation.*` transport) — or FM self-initiating from bootstrap configuration at startup (ADR-0043). Either way, FM mints `federationContextId`, owns all federation state, and alone executes the outbound OPG.04 handshake. (A partner OP establishing federation *with us* arrives inbound on the canonical `/operatorplatform/federation/v1/*` prefix via the partner-facing ingress straight to FM — a distinct path OEG never serves.)
- **Establishing a new federation is triggered two ways:** an operator-initiated call to one of the eight federation-lifecycle operations OEG serves for the outbound direction on its own platform namespace, which OEG proxies to FM over a synchronous internal HTTP request carrying the raw GSMA payload and query string byte-for-byte (no DataBus, no new schema, no reshaping; ADR-0051, keeping ADR-0046's transport, superseding ADR-0043's `command.fm.federation.*` transport) — or FM self-initiating from bootstrap configuration at startup (ADR-0043). Either way, FM stores the partner-issued `federationContextId`, owns all federation state, and alone executes the outbound OPG.04 handshake. (A partner OP establishing federation *with us* arrives inbound on the canonical `/operatorplatform/federation/v1/*` prefix via the partner-facing ingress straight to FM — a distinct path OEG never serves.)
- **For zone-based deployment targeting, SRM — not OEG — determines locality and hands off to FM (ADR-0044).** SRM checks its own zone inventory first, queries FM for federated zone data when the target isn't found locally, and forwards the deploy using FM's internal schema. This is an interim layer pending a future "runtime coordinator" component (see [platform/future-architecture.md](/platform/future-architecture.md) §6.1). OEG's outbound hand-off to FM (below) is now scoped to subscriber-identifier (MSISDN/IP) routing only.

```text
@@ -298,9 +298,9 @@ An operator onboards, updates, or decommissions a federation partner.
- **Main flow:**
  1. The lifecycle request reaches FM as a synchronous internal HTTP call from OEG carrying the raw GSMA payload byte-for-byte (§L.3b; ADR-0051), or FM self-initiates from bootstrap config; profile fields are the same either way (EWBI/token endpoints, inbound and outbound OAuth2 client IDs, mounted-secret reference, supported APIs, SLA limits).
  2. FM stores profile in `partner_ops` table.
  3. FM creates/updates federation agreement in `federation_agreements` table, minting `federationContextId` for a new federation and returning it on the synchronous response.
  3. FM creates/updates federation agreement in `federation_agreements` table.
  4. FM populates routing rules.
  5. FM uses OPG.04 federation-management operations (`CreateFederation`, `GetFederationHealth`, `RenewFederation`) with the partner.
  5. FM uses OPG.04 federation-management operations (`CreateFederation`, `GetFederationHealth`, `RenewFederation`) with the partner. On `CreateFederation` the partner mints the `federationContextId`; FM the partner-issued `federationContextId` and returns it on the synchronous response.
  6. Subsequent read/update/decommission operations on an existing partner use FM's L.2 internal admin API directly (unchanged).

### UC-FM-07: Outbound Federated Deployment via SRM (ADR-0044)
@@ -363,7 +363,7 @@ Per the component boundary matrix:
|---|---|
| EWBI termination and federation routing | **FM** (the entire `/operatorplatform/federation/v1/*` prefix for inbound partner traffic; OEG serves eight GSMA operations for the operator-triggered *outbound* direction only, on its own `/platform/v1/federation/partners/{partnerOpId}/federations/...` namespace, as a byte-for-byte proxy back to FM — ADR-0051) |
| EWBI OAuth2 authentication/authorization (and future mTLS sender-binding) | **FM** |
| `federationContextId` minting and all federation state | **FM** |
| `federationContextId` (minted for inbound federations, stored for outbound) and all federation state | **FM** |
| Topology hiding for federation responses | **FM** |
| Federation usage-record generation toward the charging integration | **FM** |
| Async command dispatch (`command.srm.*` publishing) | **FM** (shared with OEG) |
+15 −5
Changes for fm/requirements-and-design.md: 15 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ status: "ETSI SDG OOP"
| REQ-FED-17 | The system shall emit structured JSON logs (via oop-commons) including `correlation_id`, `federation_correlation_id`, `partner_op_id`, EWBI method/path, and outcome for all federation operations. | Platform baseline |
| REQ-FED-18 | *(later phase)* The system shall produce secure federation usage/charging records and integrate them with the operator-controlled charging system according to configured charging factors. FM does not perform inter-operator financial settlement. | OPG.02 v10.0 §4.3.3 and Annex G |
| REQ-FED-19 | The system shall provide an internal admin API (L.2) for the full partner onboarding, update, and decommission lifecycle, and for managing routing rules. For partner OP profiles and federation agreements specifically, the operator-triggered create/read/update/decommission lifecycle normally arrives via OEG's synchronous federation-lifecycle proxy (§N.7, §L.3b; ADR-0051) or, for establishment, FM's own bootstrap configuration; L.2's `POST /internal/partners` / `/internal/agreements` and the corresponding `GET`/`PATCH`/`DELETE` remain available for operational/break-glass use only. Routing rules are out of that scope (they serve subscriber-identifier routing, not federation establishment) — L.2's `POST /internal/routing-rules` remains their sole, primary creation path. | OA UC-FM-06, ADR-0051 |
| REQ-FED-20 | The system shall expose internal HTTP endpoints under `/internal/*` (§L.3b), mirroring OEG's `/platform/v1/federation/partners/{partnerOpId}/federations/...` paths 1:1, that accept the eight operator-triggered outbound federation-lifecycle operations proxied synchronously by OEG (`CreateFederation`, `GetFederationContextId`, `GetFederationDetails`, `DeleteFederationDetails`, `GetZoneData`, `ZoneSubscribe`, `GetZoneDetails`, `ZoneUnsubscribe`; `UpdateFederation` out of scope), receiving the raw GSMA request payload and query string OEG forwards byte-for-byte (no OEG↔FM envelope, no reshaping). This hop is protected only by cluster-internal network-policy reachability, not a dedicated credential — no `aud`/token check is added (ADR-0042 covers EWBI partner-facing authorization only, not this hop). FM shall resolve `partnerOpId` against the partner's registration record (REQ-FED-19) and reject a `federationContextId` presented against the wrong partner, validate and execute the outbound OPG.04 operation, and — for `CreateFederation` — mint `federationContextId` and return the full `FederationResponseData` on the synchronous response. It shall support an equivalent self-initiated bootstrap path from startup configuration. OEG does not publish `command.fm.federation.*`, and inbound EWBI termination on the canonical prefix is unchanged (ADR-0051, keeping ADR-0046's synchronous-HTTP transport and superseding ADR-0043's DataBus transport). | ADR-0051 |
| REQ-FED-20 | The system shall expose internal HTTP endpoints under `/internal/*` (§L.3b), mirroring OEG's `/platform/v1/federation/partners/{partnerOpId}/federations/...` paths 1:1, that accept the eight operator-triggered outbound federation-lifecycle operations proxied synchronously by OEG (`CreateFederation`, `GetFederationContextId`, `GetFederationDetails`, `DeleteFederationDetails`, `GetZoneData`, `ZoneSubscribe`, `GetZoneDetails`, `ZoneUnsubscribe`; `UpdateFederation` out of scope), receiving the raw GSMA request payload and query string OEG forwards byte-for-byte (no OEG↔FM envelope, no reshaping). This hop is protected only by cluster-internal network-policy reachability, not a dedicated credential — no `aud`/token check is added (ADR-0042 covers EWBI partner-facing authorization only, not this hop). FM shall resolve `partnerOpId` against the partner's registration record (REQ-FED-19) and reject a `federationContextId` presented against the wrong partner, validate and execute the outbound OPG.04 operation, and — for `CreateFederation` — store the partner-issued `federationContextId` and return the full `FederationResponseData` on the synchronous response. It shall support an equivalent self-initiated bootstrap path from startup configuration. OEG does not publish `command.fm.federation.*`, and inbound EWBI termination on the canonical prefix is unchanged (ADR-0051, keeping ADR-0046's synchronous-HTTP transport and superseding ADR-0043's DataBus transport). | ADR-0051 |
| REQ-FED-21 | The system shall expose an internal endpoint for SRM to query federated zone/capability data (`shared_resource_catalogues`) by `zone_id`, and shall accept a federated-deploy hand-off from SRM for zone-based deployment targets that resolve to a partner, executing the outbound OPG.04 `InstallApp` handshake through the same pipeline used for inbound federation-originated deploys. Exact hand-off transport is open — see [fm/overview-and-architecture.md §G4](/fm/overview-and-architecture.md). | ADR-0044, OA UC-FM-07 |

---
@@ -524,7 +524,7 @@ corrects its scope, payload, and namespace per ADR-0051 (amended 2026-09-15 afte
its own first version). OEG applies edge concerns (TLS, operator auth, `correlation_id`,
timing/status logging) and forwards the raw GSMA request body and query string **unaltered**; it
builds, parses, and maps nothing. FM validates the payload, performs all agreement checks,
persistence, topology hiding, `federationContextId` minting, and the outbound partner handshake.
persistence, topology hiding, the outbound partner handshake, and storing the partner-issued `federationContextId`.

This path carries only operator-triggered *outbound* federation lifecycle. Inbound EWBI (partner
OPs calling `/operatorplatform/federation/v1/*` at us) is unchanged — it terminates at FM, which
@@ -542,7 +542,7 @@ The operator-facing surface OEG exposes is its **own** platform namespace,

| Method | Path | OPG.04 operation | FM action |
|---|---|---|---|
| `POST` | `/partners/{partnerOpId}/federations` | `CreateFederation` | validate, persist, mint `federationContextId`, drive outbound `CreateFederation` |
| `POST` | `/partners/{partnerOpId}/federations` | `CreateFederation` | validate, persist, drive outbound `CreateFederation`, store the partner-issued `federationContextId` |
| `GET` | `/partners/{partnerOpId}/federations` | `GetFederationContextId` | resolve the federation-context id |
| `GET` | `/partners/{partnerOpId}/federations/{federationContextId}` | `GetFederationDetails` | read the federation record |
| `DELETE` | `/partners/{partnerOpId}/federations/{federationContextId}` | `DeleteFederationDetails` | decommission, drive outbound `DeleteFederationDetails` |
@@ -577,8 +577,8 @@ use only (REQ-FED-19).
  **byte-for-byte**, query string included. OEG does no reshaping; FM owns OPG.04 validation and
  execution.
- **Response:** FM's raw GSMA response, relayed by OEG **byte-for-byte**. For
  `POST /partners/{partnerOpId}/federations` FM creates the federation, mints
  `federationContextId`, and returns the full `FederationResponseData` on the same synchronous
  `POST /partners/{partnerOpId}/federations` FM creates the federation, stores the
  partner-issued `federationContextId`, and returns the full `FederationResponseData` on the same synchronous
  response — there is no deferred-result channel.
- **Secret handling:** generalizes rather than naming a field — on this path OEG **must not log or
  persist the request or response body** — only method, path, status, `X-Correlator`, and timing
@@ -597,6 +597,15 @@ use only (REQ-FED-19).
  not CAMARA `ErrorInfo` (OEG interface-contract §K.6a, ADR-0050): `400` (operator body fails
  schema), `401`/`403` (operator JWT/role), `503` (FM unreachable or times out), `502` (FM
  answered with something OEG cannot use). `X-Correlator` is set on all of them.
- **FM's status set on `/internal/partners/{partnerOpId}/federations/...`:** only the statuses
  OPG.04 v1.4.0 declares for these operations (`200`, `400`, `401`, `404`, `409`, `422`, `500`,
  `503`, `520`), every error as `application/problem+json` `ProblemDetails`; OEG turns anything
  else into its `502`. FM maps its EWBI-side statuses here, keeping the cause in `type`: partner
  unreachable → `503`, partner response invalid → `520`, `partner-not-active` and
  `agreement-expired` → `409`, `agreement-violation` → `422`, request validation → `400` with
  `invalidParams`. A partner rejection is relayed only as `400`/`404`/`409`/`422` (always
  `problem+json`); any other partner status becomes `520` `partner-rejected`, so a partner's
  `401` or `5xx` never reads as the operator's or FM's own.

### L.4 FM → SRM Internal HTTP

@@ -747,3 +756,4 @@ A locally connected App Provider queries device location for a subscriber on a p
| 2.0 | 2026-09-08 | ADR-0051 corrects ADR-0046's scope/payload/namespace for the OEG-proxied surface (transport unchanged). §L.3b retitled "Federation-Lifecycle Proxy": now seven canonical OPG.04 operations (create/read/update/delete federation, `GetFederationContextId`, zone subscribe/unsubscribe) served by OEG on `/operatorplatform/federation/v1/*` for the operator-triggered *outbound* direction, with the raw GSMA payload forwarded byte-for-byte (no reshaping, no §L.2 admin shape) and a no-log/no-persist rule for the credential-bearing body. Inbound partner traffic on the same paths still terminates at FM — the two directions split at the ingress. REQ-FED-19/20 and the L.2 note rewritten; priority federation-management table note updated. |
| 2.0 | 2026-09-16 | ADR-0051 amended after FM-side review, reversing this file's own previous entry above: OEG's federation-lifecycle proxy goes back onto its own `/platform/v1/federation/partners/{partnerOpId}/federations/...` namespace, not the canonical OPG.04 prefix — the canonical URL loses partner addressing once there's an OEG hop in front of FM. §L.3b, the priority federation-management table, and REQ-FED-19/20 rewritten: eight operations now (`GetZoneData` added, `UpdateFederation` out of scope), query string forwarded, spec pinned to GSMA OPG.04 v6.0 / artifact v1.4.0, `partnerCallbackCredentials` dropped (not a v1.4.0 field), partner resolution tied to FM's not-yet-built partner registration (federation-manager#20). The "FM authenticates OEG's platform service credential (ADR-0042)" auth claim is corrected everywhere it appeared: this hop has no dedicated credential, only cluster-internal network-policy reachability; ADR-0042 is EWBI partner-facing auth only. ADR-0047/0048 renumbered to ADR-0050/0051. |
| 2.0 | 2026-09-28 | Federation Context entity: operator-triggered establishment (OEG's §L.3b proxy, ADR-0051) and bootstrap moved from the inbound sentence to the outbound one — on outbound the partner issues `federationContextId` and FM stores it; the stale ADR-0046 citation replaced with ADR-0051. |
| 2.0 | 2026-09-29 | §L.3b: FM's status set on the `/internal` federation-lifecycle routes pinned to OPG.04 v1.4.0's declared statuses, with the mapping for FM's EWBI-side `403`/`502` and for partner rejections (REQ-FED-14; OEG §K.6a's `502` gate relies on it). |
+6 −6

File changed.

Preview size limit exceeded, changes collapsed.

+3 −2
Changes for oeg/interface-contract.md: 3 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -272,7 +272,7 @@ is the sole exception to §K.6 — it does not extend to any other OEG endpoint.
| Operator request body fails schema validation | `400` | OEG-authored `ProblemDetails` |
| FM returns an OPG.04 error | passed through unchanged | FM's `ProblemDetails` |
| FM unreachable / connection timeout | `503` | OEG-authored `ProblemDetails` |
| FM answered but the response is unusable (unparseable, or a status/shape not in FM's contract) | `502` | OEG-authored `ProblemDetails` |
| FM answered but the response is unusable (unparseable, or a status/shape not in FM's contract — FM RD §L.3b) | `502` | OEG-authored `ProblemDetails` |

OEG relays FM's error bodies **byte-for-byte** — no rewrite, re-key, or re-wrap, and no
translation to `ErrorInfo`. OEG's own `ProblemDetails` follow §L.5's field meanings (`type`,
@@ -724,7 +724,7 @@ through any ingress.
   `federation_agreements`, `federation_contexts`), topology hiding, and the outbound OPG.04
   handshake with the partner's FM.
4. FM returns its raw GSMA response synchronously. For `POST /partners/{partnerOpId}/federations`
   FM creates the federation, mints `federationContextId`, and returns the full
   FM creates the federation, stores the partner-issued `federationContextId`, and returns the full
   `FederationResponseData` on this response.
5. OEG relays FM's response body and status to the operator **byte-for-byte** (correlation via the
   `X-Correlator` response header per §K.6).
@@ -821,3 +821,4 @@ from startup configuration (ADR-0043).
| 2.0 | 2026-09-10 | `command.srm.service.deploy` field list no longer includes `federation_partner_ref` — OEG never populated it and, with federation decoupled from OEG (ADR-0051), never will. The field stays in SRM's `CommandEnvelopeV1` (set by FM); a note records that OEG omits it. |
| 2.0 | 2026-09-16 | ADR-0051 amended after FM-side review (its own status now reflects this): N.7 rewritten for OEG's own `/platform/v1/federation/partners/{partnerOpId}/federations/...` namespace, not the canonical OPG.04 prefix — eight operations (`GetZoneData` added), every path scoped by a registered `partnerOpId`, query string forwarded on every call, spec pinned to GSMA OPG.04 v6.0 / artifact v1.4.0 (`partnerCallbackCredentials` removed — not a v1.4.0 field), and the OEG→FM auth step corrected to cluster-internal network-policy reachability only (no dedicated Keycloak client was built; ADR-0042 covers EWBI partner-facing auth, not this hop). K.0 and K.6a updated to match; the two directions no longer share a URL split at the ingress. ADR-0047/0048 renumbered to ADR-0050/0051 to clear a collision with `develop` and `docs/platform-roles`. |
| 2.0 | 2026-09-28 | N.7: `partnerOpId` is the UUID FM assigns when the partner is registered (FM's `/internal` routes accept nothing else); OEG rejects any other format with a `400` `ProblemDetails` before calling FM. Illustrative request's slug replaced with a UUID. |
| 2.0 | 2026-09-29 | §K.6a: "FM's contract" for the `502` row now points at FM RD §L.3b, which pins the status set FM answers with on the federation-lifecycle routes. |
Loading