Commit 079643be authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

Merge branch 'docs/fm-agreements-relay' into 'develop'

docs: align R2 federation and instance lifecycle contracts

See merge request !15
parents 0c5b6e78 7ff30a31
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"), and 2026-10-16 so a partner's one agreement is edited in place (the `PATCH` row there). 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, 2026-10-16)
**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 any of its terms in place: a partner has one agreement (a second `POST` answers `409`) |
| `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
+12 −3
Changes for decisions/0053-srm-fm-federated-lifecycle-handoff-over-the-databus.md: 12 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ status: "proposed"

# ADR-0053: SRM hands federated deploys and terminates to FM over the DataBus, and FM reports the outcome the same way

**Status:** proposed (amended 2026-09-25: SRM keeps instance rows for partner targets, superseded on 2026-10-08; 2026-09-29: `partner_not_found`; 2026-10-08: order-local `federated_handoff` instead of partner inventory)
**Status:** proposed (amended 2026-09-25: SRM keeps instance rows for partner targets, superseded on 2026-10-08; 2026-09-29: `partner_not_found`; 2026-10-08: order-local `federated_handoff` instead of partner inventory; 2026-10-15: SRM reads FM's instance before a federated terminate)
**Date:** 2026-09-22
**Affects:** [fm/overview-and-architecture.md](/fm/overview-and-architecture.md) (open item G4, UC-FM-07), [fm/requirements-and-design.md](/fm/requirements-and-design.md) (the SRM→FM hand-off section §L.3a, FM's DataBus sections §K.2 and §M), [srm/interface-contract.md](/srm/interface-contract.md) (stream configuration §D, the federated hand-off flow §F.4a), [srm/persistence-model.md](/srm/persistence-model.md) (`service_order.federated_handoff`), [platform/architecture.md](/platform/architecture.md) (DataBus configuration)
**Amends:** ADR-0001 and ADR-0022, where they make SRM the only consumer of `OOP_TASKS`; ADR-0046, where it keeps the DataBus to `command.srm.*` / `event.srm.*`
@@ -120,8 +120,17 @@ 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; if FM can't be reached, the command
is redelivered, not failed. 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.

+3 −3
Changes for decisions/index.md: 3 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -51,8 +51,9 @@ 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-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-0049](0049-data-lab-standalone-profile-shared-ingress-and-iam.md) - Data Lab is a standalone compose profile behind the platform Nginx and Keycloak (realm `dataspace`); no DataBus/SRM/OEG coupling in the catalogue release
* [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,8 +69,7 @@ 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-0049](0049-data-lab-standalone-profile-shared-ingress-and-iam.md) - Data Lab is a standalone compose profile behind the platform Nginx and Keycloak (realm `dataspace`); no DataBus/SRM/OEG coupling in the catalogue release
* [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
* [ADR-0069](0069-data-lab-helm-chart-alternative-to-compose.md) - The Data Lab can also be deployed with the OOP Helm chart: off by default, UI on a NodePort, one Virtuoso, hub and search cluster-internal
* [ADR-0070](0070-data-lab-piveau-hub-and-6gdali-map.md) - Data Lab adopts piveau-hub as catalogue engine and the 6G-DALI Metadata Application Profile (DCAT-AP 3.0 + DQV + PROV-O) as normative metadata profile
* [ADR-0071](0071-data-lab-own-version-outside-release-pin.md) - data-lab/ specs are released as 0.1.0 and are excluded from the Release 2 pin (extends ADR-0021 exceptions)
+4 −2
Changes for fm/implementation-and-delivery.md: 4 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -70,12 +70,14 @@ Settings are environment variables with the `FM_` prefix (`core/config.py`; a `.
| `FM_SRM_INTERNAL_URL` | `http://localhost:8081` | SRM internal HTTP base URL |
| `FM_KEYCLOAK_ISSUER` | `http://localhost:8090/realms/federation` | Issuer partners' tokens must carry |
| `FM_KEYCLOAK_JWKS_URL` | unset | Where FM fetches the signing keys when it can't reach the issuer URL; unset means the issuer's JWKS |
| `FM_KEYCLOAK_AUDIENCE` | `federation-manager` | Audience partner tokens must carry in `aud`. Not built yet |
| `FM_FEDERATION_ID`, `FM_COUNTRY_CODE`, `FM_MCC`, `FM_MNCS` | `oop-local`, `ES`, `214`, `07` | Our OPG.04 identity, sent on `CreateFederation` and served on `GET /internal/identity` |
| `FM_PLATFORM_CAPS` | `serviceAPIs` | Platform capabilities we advertise |
| `FM_PARTNER_STATUS_LINK` | `http://127.0.0.1:8082/operatorplatform/federation/v1/callbacks/partner-status` | Callback URL handed to partners; a deployment sets the URL partners can reach |
| `FM_INSTANCE_STATUS_LINK` | `http://127.0.0.1:8082/operatorplatform/federation/v1/callbacks/instance-status` | Same, for `onInstanceStatusEvent` after our `InstallApp` |
| `FM_AVAIL_ZONE_NOTIF_LINK` | unset | `availZoneNotifLink` for zone notifications |
| `FM_ALLOW_INSECURE_PARTNER_ENDPOINTS` | `false` | Allow plain-HTTP partner endpoints; development only |
| `FM_ZONE_OFFER_SWEEP_SECONDS` | `600` | How often every federated partner's zone offer is re-checked, besides each agreement write and each `valid_from`/`valid_until` (REQ-FED-04). Not built yet |
| `FM_BOOTSTRAP_FEDERATION` | `false` | At startup, federate with every active partner that has no outbound context (ADR-0043) |
| `FM_PARTNER_SECRET_KEY` | unset | Key (base64url, 32 bytes) that encrypts typed-in partner secrets; unset, FM accepts only secret files (ADR-0067) |

@@ -115,7 +117,7 @@ database (`adapters/database/`) and DataBus (`adapters/databus/`) modules.

FM authorizes E/WBI requests with OAuth2 Client-Credentials + signed JWT. Our Keycloak `federation` realm issues tokens for inbound partner calls; each partner's Authorization Server issues tokens for calls from our FM to that partner. FastAPI dependencies validate inbound Bearer tokens; `httpx` fetches and caches outbound tokens.

**Inbound:** a FastAPI dependency (`api/security.py`) reads the `Authorization: Bearer` header and `PartnerAuthenticator` validates the JWT signature against Keycloak's JWKS endpoint (cached, refreshed on `kid` miss), and checks issuer, expiry, and the `fed-mgmt` scope. The validated token's client identity resolves to a partner via `partner_ops`.
**Inbound:** a FastAPI dependency (`api/security.py`) reads the `Authorization: Bearer` header and `PartnerAuthenticator` validates the JWT signature against Keycloak's JWKS endpoint (cached, refreshed on `kid` miss), and checks issuer, expiry, audience (`FM_KEYCLOAK_AUDIENCE`, not built yet) and the `fed-mgmt` scope. The validated token's client identity resolves to a partner via `partner_ops`.

**Outbound:** Callers depend only on `PartnerTokenProviderPort`. The current adapter reads the partner secret from the mounted file named by `our_client_secret_ref` or, when the operator typed the secret in through the registry, decrypts it from `partner_op_secrets` (ADR-0067); it then requests a token from `token_endpoint`, caches it until near expiry, and the EWBI client attaches it as a Bearer header:

@@ -262,7 +264,7 @@ remove the FM fleet from load balancing. Tracking it (`fm_partner_federation_hea
### S.2 Integration Tests

- **FM → SRM flow:** Use Docker Compose with FM, SRM (MockAdapter), PostgreSQL, and NATS. Verify that an inbound EWBI deploy request flows through to `event.srm.operation.completed`.
- **Inbound OAuth2:** Verify JWT signature, issuer, expiry, `fed-mgmt` scope, and `azp` → `oauth2_client_id` partner resolution against a test Keycloak realm.
- **Inbound OAuth2:** Verify JWT signature, issuer, expiry, audience, `fed-mgmt` scope, and `azp` → `oauth2_client_id` partner resolution against a test Keycloak realm.
- **Outbound OAuth2:** Verify token acquisition, near-expiry caching, concurrent refresh serialization, mounted-secret rotation, token acquisition with a stored encrypted secret, and Bearer-header injection against a mock partner Authorization Server.
- **DataBus integration:** Verify `command.srm.*` publishing and `event.srm.operation.completed` consumption round-trip.

Loading