Changes for decisions/0051-oeg-fronts-federation-lifecycle-ewbi-byte-for-byte.md: 23 added lines, 9 removed lines.
Original line number
Diff line number
Diff line
@@ -9,8 +9,8 @@ status: "accepted"
# ADR-0051: OEG fronts eight GSMA federation-lifecycle operations on its own platform namespace, scoped by partner id, forwarding the raw payload byte-for-byte to FM (operator-triggered outbound only)
**Status:** accepted — amended 2026-09-15 after FM-side review of the first version. Originally drafted as ADR-0048 (and its error-model companion as ADR-0047); renumbered to 0051/0050 at 2026-09-16 to clear the collision with `develop`'s ADR-0047 (terminal-record-read) and `docs/platform-roles`'s ADR-0048 (platform-roles) — content unchanged by the renumbering.
**Date:** 2026-09-08 (amended 2026-09-15)
**Status:** accepted — amended 2026-09-15 after FM-side review of the first version, and 2026-09-24 to add partner registration to the OEG proxy (see "Partner registration and our identity"), and 2026-10-13 to add the identity read. Originally drafted as ADR-0048 (and its error-model companion as ADR-0047); renumbered to 0051/0050 at 2026-09-16 to clear the collision with `develop`'s ADR-0047 (terminal-record-read) and `docs/platform-roles`'s ADR-0048 (platform-roles) — content unchanged by the renumbering.
**Affects:**[oeg/architecture.md](/oeg/architecture.md), [oeg/interface-contract.md](/oeg/interface-contract.md), [fm/requirements-and-design.md](/fm/requirements-and-design.md), [fm/implementation-and-delivery.md](/fm/implementation-and-delivery.md), [fm/overview-and-architecture.md](/fm/overview-and-architecture.md), [platform/architecture.md](/platform/architecture.md), amends this ADR's own first version, restores the namespace part of [ADR-0046](/decisions/0046-oeg-fronts-federation-lifecycle-ewbi-as-sync-proxy.md) it had superseded, builds on [ADR-0050](/decisions/0050-oeg-federation-lifecycle-proxy-errors-are-gsma-problem-json.md), [ADR-0042](/decisions/0042-ewbi-oauth2-client-credentials-mtls-deferred.md)
## Context
@@ -64,11 +64,27 @@ and uses it to reject a `federationContextId` that belongs to a different partne
partner URL instead would let a caller point FM at an arbitrary endpoint; a registered id keeps
that choice on FM's side, by design.
Registering a partner (`POST /internal/partners` and siblings, REQ-FED-19) is FM-side work that
Changes for decisions/index.md: 1 added line, 1 removed line.
Original line number
Diff line number
Diff line
@@ -51,4 +51,4 @@ the reasoning lives here. Never renumber; supersede instead (see the record-deci
*[ADR-0046](0046-oeg-fronts-federation-lifecycle-ewbi-as-sync-proxy.md) - operator-triggered federation establishment and zone subscription proxy synchronously through OEG to FM (no DataBus, no internal schema); scope and payload unchanged from ADR-0043, inbound EWBI stays entirely with FM (supersedes ADR-0043's transport; scope/payload/namespace corrected by ADR-0051)
*[ADR-0049](0049-terminal-record-read-follows-camara-schema.md) - terminal-record read behaviour follows each family's own vendored CAMARA schema: Traffic Influence returns 200 with `state: deleted`, QoD stays 404 (amends ADR-0034's blanket 404 rule and its 204 delete code)
*[ADR-0050](0050-oeg-federation-lifecycle-proxy-errors-are-gsma-problem-json.md) - OEG's federation-lifecycle proxy endpoints (§N.7, ADR-0046) return OPG.04 `application/problem+json``ProblemDetails` for every error - FM's errors relayed unchanged, OEG's own 400/401/403/502/503 from a fixed template; ADR-0030's `ErrorInfo` rule is scoped to OEG's CAMARA northbound surface
*[ADR-0051](0051-oeg-fronts-federation-lifecycle-ewbi-byte-for-byte.md) - OEG fronts eight GSMA federation-lifecycle operations on its own platform namespace, `/platform/v1/federation/partners/{partnerOpId}/federations/...`, scoped by a registered `partnerOpId` (create/read/update/delete federation, fed-context-id lookup, zone list/subscribe/details/unsubscribe), forwarding the raw GSMA request/response byte-for-byte to FM including the query string, pinned to GSMA OPG.04 v6.0 / artifact v1.4.0; operator-triggered outbound only, separated from inbound EWBI at the ingress; keeps ADR-0046's sync-HTTP/stateless/no-DataBus transport and ADR-0050's error model (amends this ADR's own first version, which used the bare canonical OPG.04 paths and lost the partner addressing GSMA gets for free from the URL)
*[ADR-0051](0051-oeg-fronts-federation-lifecycle-ewbi-byte-for-byte.md) - OEG fronts eight GSMA federation-lifecycle operations on its own platform namespace, `/platform/v1/federation/partners/{partnerOpId}/federations/...`, scoped by a registered `partnerOpId` (create/read/update/delete federation, fed-context-id lookup, zone list/subscribe/details/unsubscribe), forwarding the raw GSMA request/response byte-for-byte to FM including the query string, pinned to GSMA OPG.04 v6.0 / artifact v1.4.0; operator-triggered outbound only, separated from inbound EWBI at the ingress; also proxies partner registration (`POST`/`GET`/`PATCH``/platform/v1/federation/partners[/{partnerOpId}]` onto FM's L.2, amended 2026-09-24) and our own federation identity (`GET /platform/v1/federation/identity`, amended 2026-10-13); keeps ADR-0046's sync-HTTP/stateless/no-DataBus transport and ADR-0050's error model (amends this ADR's own first version, which used the bare canonical OPG.04 paths and lost the partner addressing GSMA gets for free from the URL)
Changes for fm/overview-and-architecture.md: 8 added lines, 7 removed lines.
Original line number
Diff line number
Diff line
@@ -293,15 +293,14 @@ A partner operator queries device location or device status for a subscriber tha
An operator onboards, updates, or decommissions a federation partner.
-**Actor:** Operator, via OEG's synchronous federation-lifecycle proxy — the eight operations OEG serves for the outbound direction on its own platform namespace (§N.7, §L.3b; ADR-0051); FM's own internal admin API (L.2) remains available for break-glass use only.
-**Actor:** Operator, via OEG's synchronous proxy on `/platform/v1/federation/partners/...` (§N.7, §L.3b; ADR-0051) — partner registration plus the eight federation-lifecycle operations. Agreements and routing rules stay on FM's own internal admin API (L.2).
-**Trigger:** New bilateral agreement signed, OAuth2 credential rotation, endpoint change, or partner decommission — or FM's own bootstrap configuration at startup (ADR-0043).
-**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.
4. FM populates routing rules.
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).
1.**Register the partner.** The operator calls `POST /platform/v1/federation/partners`; OEG forwards the L.2 partner body byte-for-byte to `POST /internal/partners`. Profile fields: name, MCC-MNC, the partner's EWBI base URL, the client ID we issued the partner, and for outbound calls our client ID at the partner, its secret (a mounted-file reference, or the value itself, stored encrypted; ADR-0067) and the partner's token endpoint.
2. FM stores the profile in `partner_ops`; the id it assigns is the `partnerOpId`.
3. Agreements and routing rules are not proxied: they stay on L.2 (not built in FM yet) and are not needed to establish a federation.
4.**Establish the federation.** The operator calls `POST /platform/v1/federation/partners/{partnerOpId}/federations` with the raw GSMA `FederationRequestData`, its identity fields read from `GET /platform/v1/federation/identity` (§L.3b); OEG forwards it byte-for-byte to FM, which runs the outbound OPG.04 `CreateFederation` with the partner, stores the partner-issued `federationContextId`, and returns `FederationResponseData` on the same call. FM's bootstrap configuration (ADR-0043) runs this step itself at startup for every active registered partner without an outbound federation; it does not register partners. Zone subscription follows the same way (`.../zones`).
5.**Later changes** go through OEG too: `PATCH /platform/v1/federation/partners/{partnerOpId}` for credential rotation, endpoint changes, or retiring the partner (`status` → `suspended`/`decommissioned`; there is no delete), and `DELETE .../federations/{federationContextId}` to end a federation.
### UC-FM-07: Outbound Federated Deployment via SRM (ADR-0044)
@@ -429,3 +428,5 @@ goes through OEG at all — see G4.
| 2.0 | 2026-09-08 | A.1 EWBI-termination row notes the Nginx EWBI ingress is not deployed today, so there is no partner-facing inbound EWBI ingress and inbound federation is a future release; the operator-triggered OEG→FM internal path (§L.3b) works today regardless. Clarification, no behaviour change. |
| 2.0 | 2026-09-08 | ADR-0051 corrects ADR-0046's scope/payload/namespace (transport kept): OEG serves **seven** canonical OPG.04 federation-lifecycle operations (create/read/update/delete federation, `GetFederationContextId`, zone subscribe/unsubscribe) for the operator-triggered *outbound* direction, on `/operatorplatform/federation/v1/*`, forwarding the raw GSMA payload **byte-for-byte** — FM no longer "maps an operator-facing payload to OPG.04". Inbound partner traffic on the same paths still terminates at FM; the two directions split at the ingress. Intro bullets, A.1 EWBI-termination row, UC-FM-06, dependency and boundary-matrix rows 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 surface moves back to its own `/platform/v1/federation/partners/{partnerOpId}/federations/...` namespace, not the canonical prefix — the two directions no longer share a URL or split at the ingress at all. Eight operations now (`GetZoneData` added). The "authenticated by OEG's platform service credential" language is corrected everywhere it appeared: this hop has no dedicated credential, only cluster-internal network-policy reachability. Intro bullets, A.1 EWBI-termination row, UC-FM-06, dependency and boundary-matrix rows updated again. ADR-0047/0048 renumbered to ADR-0050/0051. |
| 2.0 | 2026-09-24 | UC-FM-06 rewritten: partner registration goes through OEG onto L.2 `/internal/partners` (the id FM assigns is the `partnerOpId`), agreements and routing rules stay on L.2 directly, federation establishment and later changes go through OEG. Removes the contradictory "read/update/decommission via L.2, unchanged" step. ADR-0051, amended 2026-09-24. |
| 2.0 | 2026-10-13 | UC-FM-06 matches the built FM: the profile fields are the partner API's, the secret may be typed in (ADR-0067); agreements and routing rules are not needed to federate (and not built yet); bootstrap federates with partners already registered, it does not register them; `CreateFederation`'s identity fields come from OEG's `GET /platform/v1/federation/identity`. ADR-0051, amended 2026-10-13. |
| **Rate limiting and usage metering** | Per-subscription rate limits enforced by OEG runtime controls. API usage records in PostgreSQL for billing/analytics. |
| **Catalog read-through** | Proxy SRM catalog reads for a consistent NBI surface. Invalidate cache on `event.srm.catalog.updated` events. |
| **Federation-lifecycle proxy (ADR-0051)** | Serve eight federation-lifecycle operations under OEG's own `/platform/v1/federation/partners/{partnerOpId}/federations/...` (§N.7: `CreateFederation`, `GetFederationContextId`, `GetFederationDetails`, `DeleteFederationDetails`, `GetZoneData`, `ZoneSubscribe`, `GetZoneDetails`, `ZoneUnsubscribe`; `UpdateFederation` out of scope; `operator` role) for the operator-triggered *outbound* direction, and forward each to FM's mirrored `/internal/*` endpoint over a synchronous internal HTTP call — passing the raw GSMA request, query string, and FM's response through **byte-for-byte**, no DataBus, no OEG↔FM internal schema, no `operations` row, no GSMA/OPG.04 mapping or reshaping in OEG, and no logging/persisting the request/response body. Protected only by cluster-internal network-policy reachability, no dedicated credential; FM validates and executes the OPG.04 operation, stores the partner-issued `federationContextId`, and returns it on the same call. Inbound partner traffic terminates at FM's own canonical EWBI prefix — a separate surface OEG never serves. See UC-OEG-06. |
| **Federation-lifecycle proxy (ADR-0051)** | Serve eight federation-lifecycle operations under OEG's own `/platform/v1/federation/partners/{partnerOpId}/federations/...` (§N.7: `CreateFederation`, `GetFederationContextId`, `GetFederationDetails`, `DeleteFederationDetails`, `GetZoneData`, `ZoneSubscribe`, `GetZoneDetails`, `ZoneUnsubscribe`; `UpdateFederation` out of scope; `operator` role) for the operator-triggered *outbound* direction, plus partner registration and the identity read (`/platform/v1/federation/partners[/{partnerOpId}]`, `/platform/v1/federation/identity`), and forward each to FM's mirrored `/internal/*` endpoint over a synchronous internal HTTP call — passing the raw GSMA request, query string, and FM's response through **byte-for-byte**, no DataBus, no OEG↔FM internal schema, no `operations` row, no GSMA/OPG.04 mapping or reshaping in OEG, and no logging/persisting the request/response body. Protected only by cluster-internal network-policy reachability, no dedicated credential; FM validates and executes the OPG.04 operation, stores the partner-issued `federationContextId`, and returns it on the same call. Inbound partner traffic terminates at FM's own canonical EWBI prefix — a separate surface OEG never serves. See UC-OEG-06. |
| **Callback delivery** | POST CloudEvent callbacks (`application/cloudevents+json`) to registered sinks on terminal operation state; authenticate with `Authorization: Bearer` using the request's `sinkCredential` (ADR-0008). Retry through an implementation-specific background mechanism. |
| **CAMARA `ErrorInfo` error model** | On OEG's CAMARA northbound surface, all outward errors as CAMARA's `ErrorInfo` (`status`/`code`/`message`) with `X-Correlator` header per CAMARA Commonalities r4.3 — not RFC 7807 `application/problem+json`, which the vendored spec never defines. `X-Correlator` on error responses carries the same value as the inbound request's `x-correlator` and as `correlation_id` in internal bodies — one correlation value, generated at the edge when the client sends none, under the same header name inward and outward. **Exception:** the operator-facing federation-lifecycle proxy endpoints (§N.7, ADR-0051) are GSMA-aligned and return OPG.04 `application/problem+json``ProblemDetails` for every error — relayed and OEG-originated (interface-contract §K.6a, ADR-0050). |
validates the payload, and performs all agreement checks, persistence (`partner_ops`,
3. FM resolves `partnerOpId` against the partner's registration record, validates the payload, and performs all agreement checks, persistence (`partner_ops`,
`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
@@ -369,7 +369,7 @@ These questions are not resolved by the current architecture text. Keep them vis
| REQ-OEG-14 | The system shall expose GET `/healthz` and GET `/readyz` via `oop-commons` health router (PostgreSQL + DataBus checks for readiness). | Platform baseline |
| REQ-OEG-15 | For requests subject to privacy / consent, the system shall forward privacy-relevant headers and identifiers required by product policy to SRM without storing unnecessary PII in logs. | OPG.02 v10.0 |
| REQ-OEG-16 | The system shall persist enough context in `api_usage_records` and operation metadata to support IEC/PEC models downstream (Phase 1: record app id, API product id, timestamp; real-time CHF — 3GPP convergent charging function, see [platform/future-architecture.md](/platform/future-architecture.md) — in Phase 2). IEC/PEC are not otherwise defined in this repo; confirm exact meaning with the charging domain owner before implementation. | OPG.02 v10.0 |
| REQ-OEG-17 | The system shall serve eight federation-lifecycle operations under its own `/platform/v1/federation/partners/{partnerOpId}/federations/...` (§N.7: `CreateFederation`, `GetFederationContextId`, `GetFederationDetails`, `DeleteFederationDetails`, `GetZoneData`, `ZoneSubscribe`, `GetZoneDetails`, `ZoneUnsubscribe`; `UpdateFederation` out of scope; `operator` role) for the operator-triggered *outbound* direction and proxy each to FM's mirrored `/internal/*` endpoint synchronously, forwarding the raw GSMA request, query string, and FM's response **byte-for-byte**, protected only by cluster-internal network-policy reachability (no dedicated credential — ADR-0042 covers EWBI partner-facing auth only, not this hop); it shall not use the DataBus for these actions, reshape or map the payload to/from OPG.04, log or persist the request/response body, persist an `operations` row or any federation state, perform any GSMA/EWBI protocol logic, or validate partner/EWBI tokens (ADR-0051, keeping ADR-0046's synchronous-HTTP transport, superseding ADR-0043's async transport). Inbound partner EWBI terminates at FM's own canonical prefix, a separate surface this endpoint set does not touch. It shall not evaluate zone locality for `command.srm.service.deploy` requests — that determination is SRM's (ADR-0044, see UC-OEG-05). | ADR-0051, ADR-0046, ADR-0044 |
| REQ-OEG-17 | The system shall serve eight federation-lifecycle operations under its own `/platform/v1/federation/partners/{partnerOpId}/federations/...` (§N.7: `CreateFederation`, `GetFederationContextId`, `GetFederationDetails`, `DeleteFederationDetails`, `GetZoneData`, `ZoneSubscribe`, `GetZoneDetails`, `ZoneUnsubscribe`; `UpdateFederation` out of scope; `operator` role) for the operator-triggered *outbound* direction, plus partner registration (`POST`/`GET /platform/v1/federation/partners`, `GET`/`PATCH /platform/v1/federation/partners/{partnerOpId}`, onto FM's L.2 partner endpoints; no `DELETE`), and `GET /platform/v1/federation/identity`, and proxy each to FM's mirrored `/internal/*` endpoint synchronously, forwarding the raw request body (GSMA, or FM's L.2 partner schema for registration), query string, and FM's response **byte-for-byte**, protected only by cluster-internal network-policy reachability (no dedicated credential — ADR-0042 covers EWBI partner-facing auth only, not this hop); it shall not use the DataBus for these actions, reshape or map the payload to/from OPG.04, log or persist the request/response body, persist an `operations` row or any federation state, perform any GSMA/EWBI protocol logic, or validate partner/EWBI tokens (ADR-0051, keeping ADR-0046's synchronous-HTTP transport, superseding ADR-0043's async transport). Inbound partner EWBI terminates at FM's own canonical prefix, a separate surface this endpoint set does not touch. It shall not evaluate zone locality for `command.srm.service.deploy` requests — that determination is SRM's (ADR-0044, see UC-OEG-05). | ADR-0051, ADR-0046, ADR-0044 |
## H. Architecture Proposal
@@ -521,3 +521,5 @@ Structured logging and correlation IDs are the minimum observability baseline. P
| 2.0 | 2026-09-08 | Error-model rows (A.1 table, In-Scope bullet, REQ-OEG-04) scoped to OEG's CAMARA northbound surface and given an explicit exception: the federation-lifecycle proxy endpoints (§N.7) return OPG.04 `application/problem+json``ProblemDetails` for every error — FM's errors relayed unchanged, OEG's own `400`/`401`/`403`/`502`/`503` from a fixed template (interface-contract §K.6a). ADR-0050. |
| 2.0 | 2026-09-08 | ADR-0051 corrects ADR-0046's scope/payload/namespace for the OEG-proxied surface (transport kept). UC-OEG-06, REQ-OEG-17, the Nginx / boundary-matrix / scope rows, E6, and the intro bullets rewritten: OEG now serves **seven** canonical OPG.04 federation-lifecycle operations (create/read/update/delete federation, `GetFederationContextId`, zone subscribe/unsubscribe) under `/operatorplatform/federation/v1/*` — not three under an OEG `/platform/v1/` namespace — for the operator-triggered *outbound* direction, forwarding the raw GSMA payload **byte-for-byte** with no reshaping and no logging/persisting the credential-bearing body. Inbound partner traffic on the same paths still terminates at FM; the two directions split at the ingress. |
| 2.0 | 2026-09-16 | ADR-0051 amended after FM-side review: reverses this file's own previous entry above. OEG's federation-lifecycle surface goes back to its own `/platform/v1/federation/partners/{partnerOpId}/federations/...` namespace, not the canonical OPG.04 prefix — reusing the canonical path loses the partner-addressing signal GSMA gets for free from the URL once there is an OEG hop in front of FM. Eight operations now (`GetZoneData` added), every path scoped by `partnerOpId`, query string forwarded on every call, spec pinned to GSMA OPG.04 v6.0 / artifact v1.4.0. The OEG→FM auth step is corrected throughout: no dedicated Keycloak client was built for this hop, only cluster-internal network-policy reachability — the prior `platform service credential (ADR-0042)` language was wrong, since ADR-0042 covers EWBI partner-facing auth only. Mental Model, diagram, Core Capabilities, UC-OEG-06, In/Out of Scope, boundary matrix, E6, and REQ-OEG-17 all updated to match. ADR-0047/0048 renumbered to ADR-0050/0051. |
| 2.0 | 2026-09-24 | UC-OEG-06 and REQ-OEG-17: OEG also proxies partner registration (`POST`/`GET /platform/v1/federation/partners`, `GET`/`PATCH .../{partnerOpId}`) onto FM's L.2 partner endpoints (ADR-0051, amended 2026-09-24); the "L.2 break-glass only" note is removed. |
| 2.0 | 2026-10-13 | Federation-lifecycle proxy row, UC-OEG-06 and REQ-OEG-17: OEG also relays `GET /platform/v1/federation/identity` (ADR-0051, amended 2026-10-13); the "not built yet (federation-manager#20)" note removed from UC-OEG-06. |