Commit 33ece3c3 authored by George Papathanail's avatar George Papathanail
Browse files

update tighten ADR0046 operator triggered outbound only, FM re-validates

parent a19fa515
Loading
Loading
Loading
Loading
+63 −45
Changes for decisions/0046-oeg-fronts-federation-lifecycle-ewbi-as-sync-proxy.md: 63 added lines, 45 removed lines.
Original line number Diff line number Diff line
---
type: Decision Record
title: "ADR-0046: OEG fronts the federation-lifecycle and zone-subscription EWBI operations as a stateless synchronous HTTP proxy to FM; no DataBus, no internal schema"
description: "OEG fronts the federation-lifecycle and zone-subscription EWBI operations as a stateless synchronous HTTP proxy to FM; no DataBus, no internal schema"
title: "ADR-0046: Operator-triggered federation establishment and zone subscription proxy synchronously through OEG to FM; no DataBus, no internal schema"
description: "Operator-triggered federation establishment and zone subscription proxy synchronously through OEG to FM; no DataBus, no internal schema"
tags: [adr]
timestamp: 2026-09-08T00:00:00Z
status: "accepted"
---

# ADR-0046: OEG fronts the federation-lifecycle and zone-subscription EWBI operations as a stateless synchronous HTTP proxy to FM; no DataBus, no internal schema
# ADR-0046: Operator-triggered federation establishment and zone subscription proxy synchronously through OEG to FM; no DataBus, no internal schema

**Status:** accepted
**Date:** 2026-09-08
@@ -26,50 +26,63 @@ narrowed OEG's role to a stateless proxy and proposed carrying the OEG→FM hop
request/reply over the DataBus with two OEG-side GSMA↔internal mapper functions; that transport
and that mapping layer were never written into the specs.

The 2026-09-08 call revisited both and reversed the transport and the OEG-side mapping. Federation
establishment is request/response by nature — GSMA OPG.04 `POST /partner` has no deferred-result
channel, so the caller gets `federationContextId` on the same call — with no fan-out and no
durability requirement beyond the life of the HTTP request, so an async command family plus a
completion channel is machinery with no payoff. And an OEG↔FM internal schema is redundant: FM
already parses OPG.04 payloads for the operations it terminates directly, so having OEG re-shape
the same payload into an internal form only for FM to re-derive its OPG.04 meaning is duplicated
work and a second schema to keep in lockstep.
The 2026-09-08 call revisited both and reversed the transport and the proposed OEG-side mapping.
Federation establishment is request/response by nature — the operator triggers it and expects
`federationContextId` back on the same call — with no fan-out and no durability requirement beyond
the life of the HTTP request, so an async command family plus a completion channel is machinery
with no payoff. And a new OEG↔FM internal schema is redundant: FM's L.2 internal admin API already
defines the partner/agreement payload shape, so OEG can forward that shape unchanged and let FM
map it to OPG.04 — the 2026-09-07 plan's two OEG-side GSMA↔internal mapper functions are
duplicated work and a second schema to keep in lockstep.

Options considered:

- Option A: Keep ADR-0043 as written — async `command.fm.federation.*` over the DataBus, OEG
  persists an operation record, inbound `POST /partner` stays direct-to-FM.
- Option B: OEG fronts the federation-lifecycle and zone-subscription EWBI operations and proxies
  each one to FM over a synchronous internal HTTP call, forwarding the OPG.04 request body
  unchanged and relaying FM's response unchanged, holding no federation state — no DataBus, no
  internal schema.
  persists an operation record.
- Option B: Keep ADR-0043's operator-facing trigger and its payload, but carry the OEG→FM hop as
  a synchronous internal HTTP call instead of an async DataBus command — OEG relays FM's response
  unchanged, holds no federation state, writes no `operations` row.

## Decision

Option B. OEG terminates the federation-lifecycle and zone-subscription EWBI operations and
forwards each one to FM over a synchronous call to FM's internal HTTP API, passing the OPG.04
request body through unchanged and relaying FM's response unchanged. The
`command.fm.federation.*` subject family is not created; the DataBus is not used for federation
establishment, and stays scoped to the `command.srm.*` / `event.srm.*` flows FM already uses.

The operations OEG fronts are `CreateFederation` (`POST /partner`), `GetFederationDetails` /
`UpdateFederation` / `DeleteFederationDetails` (`GET` / `PATCH` / `DELETE
/{federationContextId}/partner`), `GetFederationContextId` (`GET /fed-context-id`), and
`ZoneSubscribe` / `ZoneUnsubscribe` (`POST` / `DELETE /{federationContextId}/zones`). Every other
EWBI operation — `GetFederationAPIs`, `InstallApp` and the rest of application LCM, `APIForwarding`
and the Service API session operations, `GetFederationHealth`, `RenewFederation`,
`GetPlatformCapabilities` — is unaffected: Nginx routes it straight to FM and OEG never sees it.
Option B — a transport change to ADR-0043, nothing more. **The scope, the operator-facing
endpoint, and the payload are exactly ADR-0043's**; only the OEG→FM mechanism changes from async
`command.fm.federation.*` over the DataBus to a synchronous internal HTTP call. The
`command.fm.federation.*` subject family is not created; the DataBus stays scoped to the
`command.srm.*` / `event.srm.*` flows FM already uses.

**Scope — operator-triggered outbound only.** OEG exposes an operator-facing trigger for standing
up a new *outbound* federation and for managing its zone subscriptions, under OEG's own platform
namespace (`POST /platform/v1/federation/partners` and the `.../zones` sub-resource, ADR-0020),
gated by an operator JWT with the `operator` role. Reading, updating, and decommissioning an
existing federation stay on FM's L.2 internal admin API directly (REQ-FED-19) — ADR-0046 does not
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.

**Inbound EWBI termination is unchanged.** Partner OPs calling `/operatorplatform/federation/v1/*`
directly (`CreateFederation`, `InstallApp` and application LCM, `APIForwarding`, all of it) are
routed by Nginx straight to FM, exactly as before — no path split, OEG is not on that path. FM
validates the partner `fed-mgmt` token (REQ-FED-01) and executes OPG.04 for those requests.

**Auth on the OEG→FM internal call.** FM authenticates the internal call itself. OEG presents a
platform service credential — an OAuth2 client-credentials token from the platform Keycloak
(ADR-0042) — which FM validates on `/internal/federation/*`. OEG forwards no resolved partner
identity and no `partner_op_id`; it forwards only `X-Correlator`. `fm:8082/internal/*` is never
exposed through Nginx or an Ingress — it is reachable only cluster-internally. Client mTLS on this
hop is deferred defense-in-depth (ADR-0042).

OEG holds no federation state for any of this — no `operations` row, no `OperationType` value, no
domain model, repository, migration, or completion consumer; the only "state" is the in-flight
HTTP request awaiting FM's reply. OEG applies its normal edge concerns (TLS termination,
HTTP request awaiting FM's reply. OEG applies its normal edge concerns (TLS termination, operator
authentication, correlation-id propagation, structured logging) and nothing else. FM performs all
OPG.04 parsing, validation, agreement checks, topology hiding, and persistence, and owns all
federation state including `federationContextId` — on `CreateFederation` it mints the
`federationContextId` and returns the full `FederationResponseData` on the same synchronous
response, which OEG relays to the partner. FM remains the sole owner of OPG.04 protocol execution,
inbound and outbound, including the outbound `CreateFederation` / `ZoneSubscribe` handshake with a
partner FM.
federation state including `federationContextId`. FM remains the sole owner of OPG.04 protocol
execution, inbound and outbound, including the outbound `CreateFederation` / `ZoneSubscribe`
handshake with a partner FM.

This supersedes ADR-0043's transport and its operation-record requirement. ADR-0043's remaining
substance stands: federation establishment is a product-facing action routed through OEG for
@@ -83,24 +96,29 @@ What becomes easier:
- No new DataBus subject family, no stream or consumer topology to design, no completion channel.
  ADR-0043's open item — whether FM gets a second consumer binding on `OOP_TASKS` or a dedicated
  stream — disappears.
- One payload shape end to end (GSMA OPG.04). There is no OEG↔FM internal schema to define,
  version, or keep in lockstep, and the 2026-09-07 mapper stubs are not built.
- No new schema: OEG forwards the L.2 admin payload shape FM already defines, so there is nothing
  to version or keep in lockstep, and the 2026-09-07 OEG-side mapper stubs are not built.
- OEG stays genuinely thin for federation: a routing and edge-concern layer with no
  federation-domain code, model, or persistence.
- `federationContextId` reaches the partner on the same call, matching GSMA OPG.04's synchronous
  `CreateFederation` contract without OEG bridging an async result back onto a synchronous response.
- `federationContextId` reaches the operator on the same call, so OEG never bridges an async
  result back onto a synchronous response.

What becomes harder:

- FM must expose an internal HTTP surface for OEG to call, distinct from the EWBI surface it
  terminates directly; the endpoint paths and bodies are specified in
  [fm/requirements-and-design.md](/fm/requirements-and-design.md) §L.3b.
- Federation establishment now couples OEG's request path to FM availability — if FM is down, a
  partner's `CreateFederation` fails synchronously rather than being queued. Acceptable: GSMA
  OPG.04 already models this as a synchronous operation, and there is no requirement to accept a
  federation request while FM is unavailable.
- Nginx routing for `/operatorplatform/federation/v1/*` is now split by path: the
  federation-lifecycle and zone-subscription operations terminate at OEG, the rest at FM.
- The operator's establishment request now couples OEG's request path to FM availability — if FM
  is down it fails synchronously rather than being queued (ADR-0043's `PENDING` `operations` row
  is gone). Acceptable: there is no requirement to accept a federation-establishment request while
  FM is unavailable.
- FM must authenticate the OEG→FM internal call (platform service credential, ADR-0042) and keep
  `/internal/*` off any Nginx/Ingress route.

What stays the same:

- Inbound EWBI is untouched — no Nginx path split; `/operatorplatform/federation/v1/*` still
  terminates entirely at FM, which validates the partner token and runs OPG.04.

Deferred or excluded:

+1 −1
Changes for decisions/index.md: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -48,4 +48,4 @@ the reasoning lives here. Never renumber; supersede instead (see the record-deci
* [ADR-0043](0043-oeg-proxied-federation-establishment.md) - operator-triggered federation establishment is proxied through OEG over the DataBus (command.fm.federation.*); FM also supports config-driven bootstrap federation (transport superseded by ADR-0046)
* [ADR-0044](0044-srm-owns-federated-zone-locality-and-hand-off.md) - SRM resolves Edge Cloud Zone locality for deployment targets and forwards non-local deploys to FM; interim layer pending a future runtime-coordinator component
* [ADR-0045](0045-pin-narrows-candidates-not-probing.md) - a zone/domain pin narrows the device-targeted candidate set rather than bypassing target probing; probing is triggered by how many candidates survive the pin (amends ADR-0038's unpinned scoping)
* [ADR-0046](0046-oeg-fronts-federation-lifecycle-ewbi-as-sync-proxy.md) - OEG fronts the federation-lifecycle and zone-subscription EWBI operations as a stateless synchronous HTTP proxy to FM; no DataBus, no internal schema (supersedes ADR-0043's transport)
* [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)
+12 −11
Changes for fm/overview-and-architecture.md: 12 added lines, 11 removed lines.
Original line number Diff line number Diff line
@@ -19,11 +19,11 @@ status: "ETSI SDG OOP"
If you are new to FM, this is the simplest useful way to think about it:

- **FM is the bridge to other operators.** When a request needs capabilities on a partner operator's network — or a partner operator needs capabilities on ours — FM handles the cross-boundary routing, identity validation, and response sanitization.
- **FM terminates the East/Westbound Interface (EWBI).** Nginx routes the canonical OPG.04 prefix `/operatorplatform/federation/v1/*` to FM, except the federation-lifecycle and zone-subscription operations (RD §L.3b; ADR-0046), which OEG terminates and proxies to FM's internal HTTP surface synchronously. FM still executes all OPG.04 protocol for those — OEG only passes the payload through, unchanged.
- **FM terminates the East/Westbound Interface (EWBI).** Nginx routes the whole canonical OPG.04 prefix `/operatorplatform/federation/v1/*` straight to FM — every inbound partner operation, no path split. Separately, the *operator-triggered* federation-establishment and lifecycle actions arrive from OEG as a synchronous internal HTTP call (RD §L.3b; ADR-0046); FM maps those to OPG.04 and drives the outbound handshake.
- **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:** a partner- or operator-initiated call that OEG terminates and proxies to FM over a synchronous internal HTTP request — no DataBus, no internal schema, the OPG.04 body forwarded unchanged and the response relayed unchanged (ADR-0046, 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 `CreateFederation`/`ZoneSubscribe` handshake.
- **Establishing a new federation is triggered two ways:** an operator-initiated call to OEG's federation-establishment trigger, which OEG proxies to FM over a synchronous internal HTTP request carrying the operator-facing payload (no DataBus, no new schema; ADR-0046, 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 `CreateFederation`/`ZoneSubscribe` handshake. (A partner OP establishing federation *with us* arrives inbound on EWBI straight to FM — a separate path.)
- **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
@@ -86,7 +86,7 @@ There are two success definitions:

| Area | Current architecture position |
|---|---|
| **EWBI termination** | Terminate inbound EWBI HTTPS requests routed via Nginx under the OPG.04 server prefix `/operatorplatform/federation/v1/*`. The federation-lifecycle and zone-subscription operations (§L.3b; ADR-0046) instead arrive as a synchronous internal HTTP call from OEG carrying the unmodified OPG.04 body — FM processes them exactly as if received on EWBI directly. Client mTLS is a future defense-in-depth layer (ADR-0042), not required today. |
| **EWBI termination** | Terminate all inbound EWBI HTTPS requests routed via Nginx under the OPG.04 server prefix `/operatorplatform/federation/v1/*` — no operation is carved out to OEG. Separately, the operator-triggered federation-establishment and lifecycle actions arrive as a synchronous internal HTTP call from OEG (§L.3b; ADR-0046), authenticated by OEG's platform service credential and mapped to OPG.04 by FM. Client mTLS is a future defense-in-depth layer (ADR-0042), not required today. |
| **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. |
@@ -293,12 +293,12 @@ A partner operator queries device location or device status for a subscriber tha

An operator onboards, updates, or decommissions a federation partner.

- **Actor:** Operator or partner OP, via OEG's synchronous federation-lifecycle proxy (ADR-0046); Portal or FM's own internal admin API (L.2) remain available for direct read/update/decommission operations on existing partner records.
- **Actor:** Operator, via OEG's synchronous federation-establishment trigger (§N.7, §L.3b; ADR-0046); Portal or FM's own internal admin API (L.2) remain available for direct read/update/decommission operations on existing partner records.
- **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 establishment/lifecycle request reaches FM as a synchronous internal HTTP call from OEG carrying the unmodified OPG.04 body (ADR-0046), 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).
  1. The establishment/lifecycle request reaches FM as a synchronous internal HTTP call from OEG carrying the operator-facing payload shape (§L.3b; ADR-0046), 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 inbound federation and returning it on the synchronous response.
  3. FM creates/updates federation agreement in `federation_agreements` table, minting `federationContextId` for a new federation and returning it on the synchronous response.
  4. FM populates routing rules.
  5. FM uses OPG.04 federation-management operations (`CreateFederation`, `GetFederationHealth`, `RenewFederation`) with the partner.
  6. Subsequent read/update/decommission operations on an existing partner use FM's L.2 internal admin API directly (unchanged).
@@ -325,7 +325,8 @@ A locally connected App Provider deploys to an Edge Cloud Zone that turns out to

### In Scope

- **EWBI termination** under `/operatorplatform/federation/v1/*` (behind Nginx) for all inbound partner operator requests, and the same processing for the federation-lifecycle and zone-subscription operations when they arrive as a synchronous internal HTTP call proxied by OEG (§L.3b; ADR-0046) instead of directly on EWBI.
- **EWBI termination** under `/operatorplatform/federation/v1/*` (behind Nginx) for all inbound partner operator requests — the entire prefix, no operation routed to OEG.
- **Operator-triggered federation establishment and lifecycle** when it arrives as a synchronous internal HTTP call proxied by OEG (§L.3b; ADR-0046), mapped to OPG.04 by FM.
- **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.
- **Inbound request dispatch** — publish `command.srm.*` commands to DataBus with `source=federation` for SRM fulfillment.
@@ -353,20 +354,20 @@ A locally connected App Provider deploys to an Edge Cloud Zone that turns out to
FM sits between **partner operators** and **internal platform services**:

- **Upstream:** Partner Operator Platforms (over EWBI)
- **Lateral:** OEG (synchronous internal-HTTP proxy for the federation-lifecycle + zone-subscription EWBI operations, ADR-0046; subscriber-identifier outbound hand-off, UC-FM-02); SRM (federated zone query and zone-based deploy hand-off, ADR-0044)
- **Lateral:** OEG (synchronous internal-HTTP proxy for the operator-triggered federation-establishment trigger, ADR-0046; subscriber-identifier outbound hand-off, UC-FM-02); SRM (federated zone query and zone-based deploy hand-off, ADR-0044)
- **Downstream:** SRM (via DataBus for mutations, internal HTTP for reads/capacity checks)

Per the component boundary matrix:

| Concern | Owner |
|---|---|
| EWBI termination and federation routing | **FM** (OEG terminates only the federation-lifecycle + zone-subscription subset and proxies it straight through, ADR-0046) |
| EWBI termination and federation routing | **FM** (the entire `/operatorplatform/federation/v1/*` prefix; OEG is not on the EWBI path — ADR-0046) |
| EWBI OAuth2 authentication/authorization (and future mTLS sender-binding) | **FM** |
| `federationContextId` minting 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) |
| Internal HTTP endpoints for OEG's synchronous federation-lifecycle proxy (ADR-0046) | **FM** (called by OEG; OPG.04 body passed through unchanged) |
| Internal HTTP endpoints for OEG's synchronous federation-establishment trigger (ADR-0046) | **FM** (called by OEG with a platform service credential FM validates; operator-facing payload mapped to OPG.04 by FM) |
| TF-SDK adapter invocation | **SRM** (not FM) |
| Placement decisions, including local-vs-federated zone determination for deployment targets (ADR-0044) | **SRM** (not FM) |
| Service and resource inventory | **SRM** (not FM) |
@@ -424,4 +425,4 @@ goes through OEG at all — see G4.
|---------|------|---------|
| 2.0 | 2026-07-07 | OOP Release 2.0. |
| 2.0 | 2026-09-04 | Added OEG-proxied federation-establishment trigger and FM bootstrap config (ADR-0043, UC-FM-06 updated). Added UC-FM-07: SRM (not OEG) now owns zone-based deployment locality and the hand-off to FM (ADR-0044); UC-FM-02/G3 narrowed to subscriber-identifier routing; added G4 for the open SRM→FM transport question. |
| 2.0 | 2026-09-08 | OEG→FM federation establishment moves from async over the DataBus (ADR-0043) to a stateless synchronous HTTP proxy (ADR-0046): OEG terminates the federation-lifecycle and zone-subscription EWBI operations and forwards the OPG.04 body to FM's internal HTTP surface unchanged; FM mints `federationContextId` and replies on the same call. EWBI-termination scope, UC-FM-06, boundaries and boundary matrix updated; `command.fm.federation.*` consumer row replaced. |
| 2.0 | 2026-09-08 | OEG→FM transport for the *operator-triggered* federation-establishment trigger moves from async over the DataBus (ADR-0043) to a synchronous internal HTTP call (ADR-0046); scope, the operator endpoint and its payload are unchanged from ADR-0043. FM authenticates OEG's platform service credential and maps the operator payload to OPG.04; FM mints `federationContextId` and replies on the same call. Inbound EWBI termination is unchanged — the whole `/operatorplatform/federation/v1/*` prefix stays with FM, no OEG carve-out. UC-FM-06, boundaries and boundary matrix updated; `command.fm.federation.*` consumer row replaced. |
+68 −57

File changed.

Preview size limit exceeded, changes collapsed.

+44 −39

File changed.

Preview size limit exceeded, changes collapsed.

Loading