Commit 7a3f7278 authored by Sergio Gimenez's avatar Sergio Gimenez
Browse files

docs: align federation and instance lifecycle contracts

R2 federation and instance lifecycle changes:

- Agreements. Without one a partner sees none of our zones, and the only way
  to write one was kubectl exec into FM. ADR-0051 now has OEG relay
  POST/PATCH /platform/v1/federation/agreements and GET .../local-zones onto
  FM's L.2, like partner registration. FM Sec. L.2 spells out the body, what
  PATCH may change, the agreements embedded in the partner detail and the new
  /internal/local-zones; OEG Sec. N.7 and K.6a list the routes.

- Zone sync (REQ-FED-04). A partner's offered zones are refreshed from every
  GetFederationDetails and from ZONES ADD/REMOVE partner-status callbacks, and
  FM sends those callbacks when an agreement change alters what a partner is
  offered. Deleting a federation releases its zone subscriptions on both sides.

- Helm onboarding (ADR-0068 amendment). A Helm unit goes to the partner as a
  GSMA HELM artefact; the images GSMA wants listed are read from the chart's
  values.yaml, since SRM records only the chart.

- Instance reads (proposed amendment). OEG supplements SRM runtime rows with
  its saved failed attempts, scoped by the originating operation's provider
  and the request filters; confirmed terminated rows are omitted. This keeps
  refused partner deployments visible and completed deletes out of the list.

- Terminate hand-off. ADR-0053 and SRM F.4a now describe the FM instance read
  that supplies zone_id, keeping target_service_instance_id null for an
  instance absent from SRM inventory.

Co-Authored-By: default avatarClaude Opus 5.5 <noreply@anthropic.com>
parent 1f0bc397
Loading
Loading
Loading
Loading
+21 −3
Changes for decisions/0051-oeg-fronts-federation-lifecycle-ewbi-byte-for-byte.md: 21 added lines, 3 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, 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.
**Date:** 2026-09-08 (amended 2026-09-15, 2026-09-24, 2026-10-13)
**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"), 2026-10-13 to add the identity read, and 2026-10-15 to add agreements and our zone list (see "Agreements and our zones"). 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, 2026-09-24, 2026-10-13, 2026-10-15)
**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
@@ -80,12 +80,30 @@ so OEG proxies it on the same namespace and under the same rules as the eight op
There is no `DELETE`: partners are retired via `status`, never removed (FM §L.2). The body is FM's
own L.2 partner schema, not a GSMA schema — it is forwarded byte-for-byte like every other operation
here, and since it can carry the partner's client secret (ADR-0067), the no-body-logging rule below
applies to it unchanged. Federation agreements and routing rules stay on FM's L.2 API.
applies to it unchanged. Routing rules stay on FM's L.2 API.

`GET /identity` exists because FM accepts `CreateFederation` only with our own identity
(`partnerStatusLink`, `origOPFederationId`, `origOPCountryCode`, and MNCs among ours) and fills in
the optional fields left out; the operator reads those values here instead of typing them.

### Agreements and our zones (added 2026-10-15)

A partner sees none of our zones and can deploy nothing until an agreement grants them (FM §L.2,
REQ-FED-05). The hosting operator records that agreement, so it belongs in the same operator
workflow as registering the partner; without a relay the only way to write one was `kubectl exec`
into FM. OEG proxies it the same way, plus the read the operator needs to pick zones:

| OEG path (under `/platform/v1/federation`) / FM path (under `/internal`) | FM action |
|---|---|
| `POST /agreements` | record what a partner may use here: our zones, the GSMA terms offered in each, the requests allowed, validity |
| `PATCH /agreements/{agreementId}` | change its `status`, usage limits or validity; other terms take a new agreement |
| `GET /local-zones` | our own zones, the ones an agreement can offer (partner zones left out) |

Agreements are read embedded in `GET /partners/{partnerOpId}`, as FM §L.2 already had it; there is
no agreement list or `DELETE`, since agreements are retired through `status`. The bodies are FM's
L.2 schema, forwarded byte-for-byte; FM validates them and answers a bad one with a `400`
`ProblemDetails` naming the field, like a partner registration.

### The eight operations

OEG serves these under its own platform namespace, `/platform/v1/federation`, gated by an operator
+10 −2
Changes for decisions/0053-srm-fm-federated-lifecycle-handoff-over-the-databus.md: 10 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -120,8 +120,16 @@ isn't built for request/reply.
| `command.fm.service.terminate` | `OOP_TASKS` | the `SrmServiceTerminateV1` SRM received, unshaped | the instance is absent from SRM's local inventory (partner-hosted; SRM never wrote a partner `service_instance`) |

The envelope keeps the original `operation_id` and `correlation_id`. SRM does no GSMA shaping; FM
alone maps to `InstallApp` and `RemoveApp` (ADR-0044). Terminate has no lookup step: FM resolves the
instance from its own `federation_transactions` records.
alone maps to `InstallApp` and `RemoveApp` (ADR-0044). FM resolves the partner instance from
its own `federation_transactions` records.

**2026-10-15 amendment:** before accepting a terminate for an instance absent locally, SRM
reads `GET /internal/federated-service-instances/{service_instance_id}` to retain its `zone_id`
in the order-local hand-off. This lets the terminal SRM event satisfy §C.2 without a partner
`service_instance` row. If FM doesn't know the instance, SRM fails before starting. The order's
`target_service_instance_id` stays null because that column references local inventory. The
request to FM remains the original command; SRM does not select an EWBI target or store partner
inventory.

Two rules make this durable:

+18 −5
Changes for decisions/0068-fm-onboards-apps-at-partners-over-ewbi-before-installapp.md: 18 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -9,8 +9,8 @@ status: "proposed"

# ADR-0068: FM onboards an application at a partner over the EWBI before the first `InstallApp`, and the partner resolves `InstallApp` from that onboarding

**Status:** proposed
**Date:** 2026-09-25
**Status:** proposed — amended 2026-10-15 to onboard Helm units (see "Helm units")
**Date:** 2026-09-25 (amended 2026-10-15)
**Affects:** [fm/requirements-and-design.md](/fm/requirements-and-design.md) (outbound deploy path, inbound onboarding operations), [fm/persistence-model.md](/fm/persistence-model.md) (`shared_resource_catalogues` resource types, `federation_agreements.zone_resources`), [ADR-0018](0018-federation-tenancy-partner-as-tenant.md) (the agreement's app mapping), [ADR-0054](0054-fm-receives-gsma-callbacks-through-one-router.md) (one more callback kind)

## Context
@@ -63,8 +63,20 @@ is treated as success.
**Only public repositories in this release.** A deployment unit whose image sits in a private
repository is refused before anything is sent. GSMA would carry the pull credential inside
`fileRepoLocation`, but OEG does not keep the App Provider's credential today (it stores only a
`secret://` reference with no store behind it), so there is nothing to send. Container units come
first; Helm units need image references SRM does not record yet.
`secret://` reference with no store behind it), so there is nothing to send.

**Helm units (added 2026-10-15).** TF-SDK's Kubernetes adapter runs only Helm, so a container-only
onboarding left no CAMARA application deployable on a partner. A Helm unit goes to the partner as
an `UploadArtefact` with descriptor type `HELM`, `repoType: PUBLICREPO` and
`artefactRepoLocation.repoURL` set to the chart. GSMA still requires every component to name its
images as uploaded files (`componentSpec.images`, at least one), and SRM records only the chart, so
FM-A reads the chart's `values.yaml` for `image` blocks (`registry`, `repository`, `tag`, with
`Chart.yaml`'s `appVersion` as the default tag; subcharts are left to their own release) and sends
one `UploadFile` per image, `fileType: DOCKER`, by reference as above. It doesn't render the
chart. A chart that isn't an http(s) archive, can't be fetched, or declares no image that way is
refused before anything is sent. FM-B accepts a public `HELM` artefact and turns it into one helm
deployment unit with the chart as `artifact_ref`, adding up its components' CPU and memory, since
one release deploys them all.

**`zoneInfo.flavourId` comes from the zone subscription.** FM-A stores the `flavoursSupported` a
partner returned on `ZoneSubscribe` and uses one of them. A zone FM-A has not subscribed to is not
@@ -102,7 +114,8 @@ specification, version, `appId`, state). FM-B keeps `file`, `artefact` and `appl
  deploys cost none.
- Deleting a specification in SRM-A does not yet deboard it at partners (`DeleteApp` is not
  called). That is left for a later change.
- Private-repository images and Helm units are refused for now, with an error that says so.
- Private-repository images are refused for now, with an error that says so; so are Helm charts
  whose images FM can't read from `values.yaml`.
- The agreement's `app_mappings` stays as the fallback for `InstallApp` until every partner
  onboards over the EWBI, and can be removed then.

+2 −2
Changes for decisions/index.md: 2 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ the reasoning lives here. Never renumber; supersede instead (see the record-deci
* [ADR-0047](0047-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-0048](0048-platform-roles.md) - three realm roles (app-provider, operator, platform-admin); new users get app-provider but call only the API products granted to them (each CAMARA scope gated by a product role); tenancy is the `organization` claim, else `sub`; development mode switches login off on localhost (amends ADR-0006's tenant identity and ADR-0035's entitlement note)
* [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; 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)
* [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) our own federation identity (`GET /platform/v1/federation/identity`, amended 2026-10-13), and agreements with the zones they can offer (`/platform/v1/federation/agreements[/{agreementId}]`, `/platform/v1/federation/local-zones`, amended 2026-10-15); 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-0053](0053-srm-fm-federated-lifecycle-handoff-over-the-databus.md) - SRM hands federated deploys and terminates to FM as `command.fm.service.deploy` / `.terminate` on `OOP_TASKS`, and FM reports the outcome as `event.fm.operation.completed` on `OOP_EVENTS`; zone lookups stay synchronous HTTP reads. `completion_basis` separates a confirmed deploy from a terminate the partner only accepted. SRM writes a `service_order` and tracks pending partner targets in order-local `federated_handoff` (no partner zone/instance inventory); `payload_snapshot` stays frozen command audit; completes the order from FM's answer. FM waits 30 minutes by default for a deploy callback. Amends ADR-0001/0022 (FM consumes `command.fm.>`) and ADR-0046's DataBus scope; resolves G4
* [ADR-0054](0054-fm-receives-gsma-callbacks-through-one-router.md) - FM receives every OPG.04 callback on `/operatorplatform/federation/v1/callbacks/{kind}`: one router that checks the `fed-mgmt-notif` scope and that `federationContextId` is an outbound context of the calling partner, then a per-kind idempotent handler. `onPartnerStatusEvent` first; `FM_PARTNER_STATUS_LINK` moves to `/callbacks/partner-status`. The `federation` realm gets `fed-mgmt-notif` as an optional scope on partner clients
* [ADR-0055](0055-topology-discovery-is-a-separate-adapter-interface.md) - topology discovery is a separate TopologyDiscoveryAdapter interface, not optional methods on CapabilityExecutionAdapter (amends ADR-0027)
@@ -68,4 +68,4 @@ the reasoning lives here. Never renumber; supersede instead (see the record-deci
* [ADR-0065](0065-portal-sessions-keyed-by-the-cookie.md) - portal login sessions live in PostgreSQL, tokens encrypted with a per-session key only the cookie holds; login attempts are single-use rows; a session lasts as long as Keycloak accepts its refresh token
* [ADR-0066](0066-api-clients-through-keycloak-client-registration.md) - developers' API clients are Keycloak clients registered by a narrow provisioner, products fixed at creation within the creator's grants, secret shown once, registration token encrypted under one app-wide key; a refused token on delete is reported; no reconciliation with Keycloak in Release 2
* [ADR-0067](0067-partner-client-secret-encrypted-in-fm-db.md) - proposed: FM accepts the partner client secret typed in through the registry and stores it AES-GCM encrypted in fm_db (`partner_op_secrets`) under one app-wide key, never returned; `our_client_secret_ref` files stay as the other source, one per partner; no key → 501 (amends FM #21's no-secret-accepted criterion)
* [ADR-0068](0068-fm-onboards-apps-at-partners-over-ewbi-before-installapp.md) - FM onboards an application at a partner over the EWBI (`UploadFile`, `UploadArtefact`, `OnboardApplication`) on the first `InstallApp` of that specification version, with derived GSMA ids, public repositories and container units only for now, and `flavourId` from the zone subscription; the partner creates the specification in its SRM under the originating partner as tenant and resolves `InstallApp` from that onboarding, replacing ADR-0018's manual app mapping. Adds `onApplicationOnboardStatusEvent` to ADR-0054's router
* [ADR-0068](0068-fm-onboards-apps-at-partners-over-ewbi-before-installapp.md) - FM onboards an application at a partner over the EWBI (`UploadFile`, `UploadArtefact`, `OnboardApplication`) on the first `InstallApp` of that specification version, with derived GSMA ids, public repositories only for now, container units and Helm charts (images read from the chart's `values.yaml`, amended 2026-10-15), and `flavourId` from the zone subscription; the partner creates the specification in its SRM under the originating partner as tenant and resolves `InstallApp` from that onboarding, replacing ADR-0018's manual app mapping. Adds `onApplicationOnboardStatusEvent` to ADR-0054's router
+37 −8

File changed.

Preview size limit exceeded, changes collapsed.

Loading