Changes for decisions/0048-platform-roles.md: 1 added line, 1 removed line.
Original line number
Diff line number
Diff line
@@ -9,7 +9,7 @@ status: "proposed"
# ADR-0048: Three platform roles, API product grants, and tenancy
**Status:** proposed
**Status:** proposed — decision 6's "`app_provider_id` is the token's `sub`" is amended by [ADR-0066](/decisions/0066-api-clients-through-keycloak-client-registration.md): the `app_provider_id` claim when present, else `sub`
Changes for oeg/architecture.md: 2 added lines, 1 removed line.
Original line number
Diff line number
Diff line
@@ -323,7 +323,7 @@ These questions are not resolved by the current architecture text. Keep them vis
-**F1. Catalog cache:** In-process TTL vs another transient cache for `event.srm.catalog.updated` invalidation? *Recommendation:* start with in-process + short TTL; move to a shared transient cache only when load requires.
-**F2. Consent / User Identity Token:** Where does the User Identity Token enter — header vs body? *Recommendation:* follow the CAMARA security profile plus OPG.02 privacy guidance; store opaque reference in `operations.metadata` JSONB, delegate validation to SRM.
-**F3. Location Retrieval's identifier-echo fallback is an unresolved guess:** when SRM's `srm_resolved_identifier` is absent, OEG echoes the first identifier the request supplied in a fixed order (`ipv4`, `phone_number`, `ipv6`, `network_access_id`) — the mapper's own docstring calls this a guess that may not match the identifier the network actually used ([oeg/interface-contract.md](/oeg/interface-contract.md) §N.4). No decision exists for why this order, or whether guessing is acceptable at all for a CAMARA field the consumer is expected to trust.
-**F4. Token identity extraction (resolved for tenancy):**`tenant_id` is the token's `organization` claim, else its `sub`, and `app_provider_id` is the `sub` (ADR-0048). Still open: no claim maps to a device, so Location Retrieval's `device` cannot be omitted the way the vendored CAMARA spec allows when the subject is derivable from the token ([oeg/interface-contract.md](/oeg/interface-contract.md) §N.4).
-**F4. Token identity extraction (resolved for tenancy):**`tenant_id` is the token's `organization` claim, else its `sub`, and `app_provider_id` is the token's `app_provider_id` claim, else its `sub` (ADR-0048, ADR-0066). Still open: no claim maps to a device, so Location Retrieval's `device` cannot be omitted the way the vendored CAMARA spec allows when the subject is derivable from the token ([oeg/interface-contract.md](/oeg/interface-contract.md) §N.4).
-**F5. Tenant scoping of reads and deletes is not enforced (deferred to the next release):** OEG stores `tenant_id` and `app_provider_id` on every record, but reads and deletes don't filter by them yet. `GET /apps` lists every tenant's apps, and reads or deletes by ID (apps, app instances, app deployments, QoD sessions) return any tenant's record. The persistence model's "`tenant_id` scopes reads and deletes" is the target; until then, isolation rests on resource IDs being random UUIDs.
## G. Functional Requirements
@@ -506,3 +506,4 @@ Structured logging and correlation IDs are the minimum observability baseline. P
| 2.0 | 2026-10-04 | Auth responsibility and REQ-OEG-02 add per-operation CAMARA scope checks; F4 resolved for tenancy (`organization` claim, else `sub`), device mapping still open; F5 records that tenant scoping of reads and deletes is deferred; REQ-OEG-09 rate limits deferred and keyed by tenant and API product (ADR-0048). |
| 2.0 | 2026-10-04 | Auth and logging match the OEG implementation: no `oop-commons`; the CAMARA scope is required for every token, including QoD sessions (stricter than QoD v0.10.1). REQ-OEG-10 and §Structured Logging list the fields OEG logs (caller `tenant_id`, `app_provider_id`, `client_id`; one `http_request` line per request) and what is not logged yet (`operation_id`). |
| 2.0 | 2026-10-06 | REQ-OEG-08: the sink token is stored encrypted, and expired/revoked tokens skip the delivery (ADR-0063). |
| 2.0 | 2026-10-09 | `app_provider_id` is the token's `app_provider_id` claim, else its `sub`, so an API client's resources belong to the developer who created it (ADR-0066). |
| `operation_type` | VARCHAR(32) | `deploy`, `scale`, `terminate`, `network_capability_activate`, `network_capability_deactivate`. The activate/deactivate split exists because teardown is its own operation with its own `operation_id` (ADR-0034); the capability family is distinguished by the record the operation points at, not by the type. |
| `status` | VARCHAR(32) | CHECK IN (`PENDING`,`IN_PROGRESS`,`COMPLETED`,`PARTIALLY_COMPLETED`,`FAILED`). Exposed on `GET /platform/v1/operations/{operationId}` as JSON field `status` with the same name and values (no mapping layer). Phase 1 uses `PENDING`→`COMPLETED`\|`PARTIALLY_COMPLETED`\|`FAILED`; `PARTIALLY_COMPLETED` when a multi-zone operation's `instances[]` has a mix of per-instance outcomes (ADR-0005); `IN_PROGRESS` activates with `event.srm.operation.status` consumption in Phase 2. Note: this column's values are UPPERCASE and exposed as-is (no mapping), unlike `app_instances.state`/`app_deployments.state` below, which are lowercase and likewise exposed as-is — each column follows its own casing convention with no transformation on read. |
| `app_registration_id` | UUID FK -> `app_registrations` | NULLABLE (not all operations concern an app); resolved from the request's `appId` (ADR-0010) |
| 2.0 | 2026-09-11 | `traffic_influences.edge_cloud_zone_id` stores the zone pinned on the activate command rather than only the requested `edgeCloudZoneId`, and scopes `selected_appInstanceId` on the Traffic Influence notification. |
| 2.0 | 2026-10-04 | `tenant_id` is the token `organization` claim, else `sub`, and `app_provider_id` the `sub` (was "JWT `client_id`"); `api_subscriptions` reserved for per-product quota and validity (ADR-0048). |
| 2.0 | 2026-10-06 | Callback tokens (ADR-0063): new `CallbackCredential` entity and `callback_credentials` table (AES-GCM, tombstoned on revoke); `callback_registrations.sink_credential_ref` removed and the table now also covers the QoD `webhook`; `callback_deliveries.state` values aligned to `delivered`/`failed`/`skipped`; Security Design and deployment env updated for the app-wide key. |
| 2.0 | 2026-10-09 | `app_provider_id` is the token's `app_provider_id` claim, else its `sub`, so an API client's resources belong to the developer who created it (ADR-0066). |