Commit a5411e03 authored by George Papathanail's avatar George Papathanail
Browse files

fix: update oeg-fm spec based on the latest decisions

parent 6a0be1d6
Loading
Loading
Loading
Loading
+1 −1
Changes for decisions/0043-oeg-proxied-federation-establishment.md: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ status: "accepted"

# ADR-0043: Operator-triggered federation establishment is proxied through OEG over the DataBus; FM also supports config-driven bootstrap federation

**Status:** accepted
**Status:** accepted — the transport (async `command.fm.federation.*` over the DataBus) and the `PENDING` operation-record requirement are superseded by [ADR-0045](/decisions/0045-oeg-fronts-federation-lifecycle-ewbi-as-sync-proxy.md), which makes the OEG→FM hop a stateless synchronous HTTP call; the rest (federation establishment is an OEG-routed product-facing action, not direct against FM's L.2 admin API; FM also supports config-bootstrap federation) stands
**Date:** 2026-09-04
**Affects:** [oeg/architecture.md](/oeg/architecture.md), [oeg/interface-contract.md](/oeg/interface-contract.md), [fm/overview-and-architecture.md](/fm/overview-and-architecture.md), [fm/requirements-and-design.md](/fm/requirements-and-design.md), [platform/architecture.md](/platform/architecture.md)

+139 −0
Changes for decisions/0045-oeg-fronts-federation-lifecycle-ewbi-as-sync-proxy.md: 139 added lines, 0 removed lines.
Original line number Diff line number Diff line
---
type: Decision Record
title: "ADR-0045: 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"
tags: [adr]
timestamp: 2026-09-08T00:00:00Z
status: "accepted"
---

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

**Status:** accepted
**Date:** 2026-09-08
**Affects:** [oeg/architecture.md](/oeg/architecture.md), [oeg/interface-contract.md](/oeg/interface-contract.md), [fm/overview-and-architecture.md](/fm/overview-and-architecture.md), [fm/requirements-and-design.md](/fm/requirements-and-design.md), [platform/architecture.md](/platform/architecture.md)

## Context

[ADR-0043](/decisions/0043-oeg-proxied-federation-establishment.md) decided that operator-triggered
federation establishment is proxied through OEG **asynchronously over the DataBus** — OEG persists
a `PENDING` `operations` row and publishes a new `command.fm.federation.create` /
`command.fm.federation.zone_subscribe` subject family, wrapping "the same payload shape FM's L.2
endpoints already define" in a thin envelope; FM consumes it. ADR-0043 also stated that the
mandatory inbound EWBI termination (partner OPs calling our `POST /partner` directly) was
unaffected and stayed with FM, reached via Nginx routing `/operatorplatform/federation/v1/*`
straight to FM.

A follow-up discussion (2026-09-07) narrowed OEG's role to a stateless proxy and proposed carrying
the OEG→FM hop as **NATS 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 mapping:

- The DataBus is the wrong tool for federation establishment. These operations are
  request/response by nature (GSMA OPG.04 `POST /partner` has no deferred-result channel — the
  caller gets `federationContextId` on the same call), there is no fan-out, and no durability
  requirement beyond the life of the HTTP request. An async command family plus a completion
  channel is machinery with no payoff here.
- An OEG↔FM internal schema (envelope or mapper stubs) is redundant. FM already parses OPG.04
  payloads for the operations it terminates directly; having OEG re-shape the same payload into an
  internal form only to have FM re-derive the OPG.04 meaning is 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 as a **synchronous internal HTTP call**, forwarding the GSMA/OPG.04 request body
  unchanged and relaying FM's response unchanged. OEG holds no federation state. No DataBus, no
  internal schema.

## Decision

Option B.

**Transport.** Every OEG→FM federation interaction is a synchronous internal HTTP request to FM
(`http://fm:8082/internal/...`). The DataBus (`command.fm.federation.*`) is not used for
federation establishment; those subjects are not created. The DataBus remains in the picture only
for the unrelated `command.srm.*` / `event.srm.*` flows FM already uses.

**No internal schema.** OEG forwards the GSMA/OPG.04 request body to FM byte-for-byte and relays
FM's response body back to the caller unchanged. OEG does not translate, re-shape, or validate
OPG.04-specific fields beyond what it needs to route. All OPG.04 parsing, validation, agreement
checks, topology hiding, and state live in FM.

**OEG is a stateless pass-through.** OEG persists nothing for these operations — 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 still applies its normal
edge concerns (TLS termination, authentication, correlation-id propagation, structured logging).

**Operations OEG fronts** (partner-initiated inbound EWBI, plus the operator-initiated
establishment trigger, all proxied synchronously to FM):

| Method / EWBI path | OPG.04 operation | Notes |
|---|---|---|
| `POST /partner` | `CreateFederation` | FM creates the federation, mints `federationContextId`, and returns the full `FederationResponseData` on the same synchronous response; OEG relays it to the partner |
| `GET /{federationContextId}/partner` | `GetFederationDetails` | pass-through |
| `PATCH /{federationContextId}/partner` | `UpdateFederation` | pass-through |
| `DELETE /{federationContextId}/partner` | `DeleteFederationDetails` | pass-through |
| `GET /fed-context-id` | `GetFederationContextId` | pass-through |
| `POST /{federationContextId}/zones` | `ZoneSubscribe` | pass-through |
| `DELETE /{federationContextId}/zones/{zoneId}` | `ZoneUnsubscribe` | pass-through |

**Operations that stay direct to FM** (Nginx routes `/operatorplatform/federation/v1/*` for these
straight to FM; OEG never sees them): `GET /federation-resources` (`GetFederationAPIs`),
`POST /{federationContextId}/application/lcm` (`InstallApp`) and the rest of application LCM,
`POST /{federationContextId}/apiservice/{serviceAPINameVal}` (`APIForwarding`) and Service API
session operations, `GET /{federationContextId}/health` (`GetFederationHealth`),
`POST /{federationContextId}/renew` (`RenewFederation`),
`GET /{federationContextId}/platform-caps` (`GetPlatformCapabilities`).

**FM ownership is unchanged.** FM remains the sole owner of OPG.04 protocol execution — inbound
and outbound — including the outbound `CreateFederation` / `ZoneSubscribe` handshake with a
partner FM. ADR-0043's FM bootstrap-config path (self-initiated federation from startup
configuration) is untouched by this decision.

This supersedes ADR-0043's transport and its operation-record requirement. ADR-0043's remaining
substance — that federation establishment is a product-facing action routed through OEG for
auth/policy/audit rather than direct against FM's L.2 admin API, and that FM also supports
config-bootstrap federation — stands.

## Consequences

What becomes easier:

- No new DataBus subject family, no stream/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). No OEG↔FM internal schema to define, version, or
  keep in lockstep; the 2026-09-07 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 needing to bridge an async result back to a sync
  response.

What becomes harder:

- FM must expose an internal HTTP endpoint surface for OEG to call (the operations in the table
  above), distinct from the EWBI surface FM terminates directly.
- 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.

Deferred or excluded:

- The exact internal FM endpoint paths and request/response bodies for the OEG→FM calls are
  finalised in [fm/requirements-and-design.md](/fm/requirements-and-design.md) §L.3b.
- No change to the SRM→FM federated-deploy hand-off (ADR-0044) or to subscriber-identifier
  routing for synchronous query APIs (UC-FM-02 / §G3).

Revisit trigger:

- A requirement to accept and durably queue federation-establishment requests while FM is
  unavailable, or a move to make any of these operations genuinely long-running, would reopen the
  transport question.
+2 −1
Changes for decisions/index.md: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ the reasoning lives here. Never renumber; supersede instead (see the record-deci
* [ADR-0040](0040-target-serveability-is-a-tri-state.md) - can_serve_target answers can_serve | unknown_subscriber | unsupported_identifier; a boolean cannot carry the 404/422 split §E.2 requires, and 404 needs positive evidence (supersedes ADR-0038's boolean)
* [ADR-0041](0041-tfsdk-owns-adapter-construction.md) - TF-SDK builds adapters behind a factory keyed by adapter kind; SRM resolves control_path_ref to an instance but never enumerates adapter kinds
* [ADR-0042](0042-ewbi-oauth2-client-credentials-mtls-deferred.md) - E/WBI authorization is OAuth2 Client-Credentials: local Keycloak for inbound, partner Authorization Server for outbound; mTLS deferred
* [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
* [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-0045)
* [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-0045](0045-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)
+26 −11
Changes for fm/overview-and-architecture.md: 26 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. No other Open Operator Platform (OOP) component touches EWBI traffic.
- **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 (`POST /partner`, `GET`/`PATCH`/`DELETE /{federationContextId}/partner`, `GET /fed-context-id`, `POST`/`DELETE /{federationContextId}/zones`), which OEG terminates and proxies to FM's internal HTTP surface synchronously (ADR-0045). FM still executes all OPG.04 protocol for those — OEG only passes the payload through.
- **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 (ADR-0043):** an operator-facing action proxied through OEG (persist + publish `command.fm.federation.*`, no orchestration in OEG), or FM self-initiating from bootstrap configuration at startup. Either way, FM alone executes the outbound OPG.04 `CreateFederation`/`ZoneSubscribe` handshake.
- **Establishing a new federation is triggered two ways:** a partner-initiated or operator-initiated call that OEG proxies to FM over a **synchronous internal HTTP request** (no DataBus, no internal schema — OEG forwards the GSMA/OPG.04 body unchanged and relays FM's response, ADR-0045, 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.
- **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/*`. Client mTLS is a future defense-in-depth layer (ADR-0042), not required today. |
| **EWBI termination** | Terminate inbound EWBI HTTPS requests routed via Nginx under the OPG.04 server prefix `/operatorplatform/federation/v1/*`. **Exception (ADR-0045):** the federation-lifecycle and zone-subscription operations (`POST /partner`, `GET`/`PATCH`/`DELETE /{federationContextId}/partner`, `GET /fed-context-id`, `POST`/`DELETE /{federationContextId}/zones`) are terminated by OEG and arrive at FM as a synchronous internal HTTP call carrying the unmodified GSMA/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. |
| **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,15 +293,15 @@ A partner operator queries device location or device status for a subscriber tha

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

- **Actor:** Operator, normally via OEG's proxied federation-establishment trigger (ADR-0043); Portal or FM's own internal admin API (L.2) remain available for direct read/update/decommission operations on existing partner records.
- **Actor:** Operator or partner OP, via OEG's synchronous federation-lifecycle proxy (ADR-0045); 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. Operator triggers creation via OEG (`command.fm.federation.create`) 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 unmodified GSMA/OPG.04 body (ADR-0045), 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.
  3. FM creates/updates federation agreement in `federation_agreements` table, minting `federationContextId` for a new inbound 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 by ADR-0043).
  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)

@@ -325,7 +325,7 @@ 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.
- **EWBI termination** under `/operatorplatform/federation/v1/*` (behind Nginx) for all inbound partner operator requests, **plus** the same processing for the federation-lifecycle and zone-subscription operations when they arrive as a synchronous internal HTTP call proxied by OEG (ADR-0045) instead of directly on EWBI.
- **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,19 +353,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 (federation-establishment trigger, ADR-0043; 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 federation-lifecycle + zone-subscription EWBI operations, ADR-0045; 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** |
| EWBI termination and federation routing | **FM** (OEG terminates only the federation-lifecycle + zone-subscription subset and proxies it straight through, ADR-0045) |
| 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) |
| Consuming `command.fm.federation.*` (ADR-0043) | **FM** (published by OEG) |
| Internal HTTP endpoints for OEG's synchronous federation-lifecycle proxy (ADR-0045) | **FM** (called by OEG; GSMA/OPG.04 body passed through unchanged) |
| 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) |
@@ -415,6 +416,19 @@ goes through OEG at all — see G4.

**Status:** unresolved. The 2026-09-01 TSC+MDL meeting decided SRM (not OEG) owns this hand-off, but did not conclude the transport. **Recommendation:** default to synchronous internal HTTP for consistency with L.4's existing FM↔SRM pattern, with SRM treating the call as part of its own already-async `command.srm.service.deploy` handling (SRM owns retry/timeout toward FM, not OEG). Revisit once FM/SRM implementation work starts on UC-FM-07.

### G5. OEG → FM Hand-off for the Federation-Lifecycle EWBI Operations (ADR-0045)

**The question:** How does OEG hand the federation-lifecycle and zone-subscription EWBI operations
(`POST /partner`, `GET`/`PATCH`/`DELETE /{federationContextId}/partner`, `GET /fed-context-id`,
`POST`/`DELETE /{federationContextId}/zones`) to FM?

**Status:** resolved (ADR-0045). Synchronous internal HTTP: OEG terminates the request, calls FM's
corresponding endpoint under `http://fm:8082/internal/...` with the GSMA/OPG.04 body forwarded
byte-for-byte, and relays FM's response unchanged. No DataBus (`command.fm.federation.*` is not
created), no OEG↔FM internal schema, no `operations` row on the OEG side. FM mints
`federationContextId` on `POST /partner` and returns it on the same response. Endpoint shapes in
[fm/requirements-and-design.md §L.3b](/fm/requirements-and-design.md).

---

## Revision History
@@ -423,3 +437,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 | ADR-0045: the OEG→FM federation-establishment hop is now a stateless synchronous HTTP proxy, not async over the DataBus. OEG terminates the federation-lifecycle + zone-subscription EWBI operations and forwards the GSMA/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 + boundary matrix updated; `command.fm.federation.*` consumer row replaced; added G5. |
+65 −28

File changed.

Preview size limit exceeded, changes collapsed.

Loading