# Platform Registry Contract v1

Status: draft for architecture review (PF-REGISTRY-CONTRACT-001)
Owner: Platform Architecture
Last updated: 2026-06-01

## Purpose

Define the first platform registry contract used by platform shared services and
product domains.

The registry is the product-neutral catalog of stable IDs, metadata, lifecycle
states, and ownership rules that shared services need before they can safely
serve multiple products. It prevents IAM, billing, audit, notification,
evidence, artifact trust, and App SDK workflows from hardcoding GPUaaS-specific
behavior in service code or seed-only app manifests.

This document is a contract and review checkpoint. It does not require physical
service extraction or a full admin UI before implementation starts.

## Scope

In scope:

- product registry;
- scope registry;
- usage-unit registry;
- audit-action registry;
- notification-template registry;
- retention-class registry;
- evidence-type registry;
- artifact-type registry;
- App SDK/developer contract registry entries;
- seed-backed versus schema-backed implementation path;
- first seed entries for GPUaaS, App Platform, platform shared services, and
  evidence/status.

Out of scope for v1:

- external self-service product onboarding;
- public marketplace publication workflow;
- per-tenant registry overrides;
- registry admin UI;
- full artifact signing/provenance policy implementation;
- replacing existing `app_catalog`, `app_versions`, `platform_policy_values`,
  `platform_audit_logs`, or artifact tables.

## Operating Principles

1. Registry IDs are stable product/platform contracts, not display labels.
2. Registry entries are versioned when behavior, authorization, billing, audit,
   release gates, or developer-visible contracts change.
3. Products contribute entries through an explicit registry contract. They do
   not directly own platform registry internals.
4. Long-lived resources should snapshot the registry version they were created
   under when the entry affects runtime behavior.
5. Unknown, disabled, or untrusted entries fail closed on authority, billing,
   artifact promotion, credential delivery, or release-blocking paths.
6. Seed-backed implementation is acceptable for the first pass only when the
   schema-backed target and migration path are documented.

## Implementation Path

| Phase | Backing model | Use |
|---|---|---|
| Phase 0 | Contract plus seed-backed config | Define entries, unblock facades, avoid hardcoded constants |
| Phase 1 | Schema-backed tables with seed import | Read models, validation, CI drift checks, and version snapshots |
| Phase 2 | Registry APIs and admin/read-only surfaces | Internal platform/product onboarding and review workflows |
| Phase 3 | Externalized/developer portal integration | Public or partner-facing registry tracks where appropriate |

Phase 0 should use repo-owned seed/config artifacts and generated validation
tests. Phase 1 should add platform-owned tables rather than overloading product
tables such as `app_catalog`.

## Entry Shape

Every registry entry has a common envelope:

| Field | Meaning |
|---|---|
| `registry_family` | `product`, `scope`, `usage_unit`, `audit_action`, `notification_template`, `policy_snapshot`, `quota_dimension`, `portal_track`, `evidence_type`, `artifact_type`, `sdk_contract` |
| `id` | stable machine-readable ID |
| `version` | semantic or date-based version |
| `display_name` | human-readable label |
| `owner_domain` | platform or product owner |
| `owner_team` | accountable team |
| `lifecycle` | `draft`, `active`, `deprecated`, `retired` |
| `risk_class` | `low`, `medium`, `high`, `critical` |
| `effective_from` | first valid timestamp/version |
| `supersedes` | previous entry version, if any |
| `metadata` | family-specific JSON contract |

## Registry Families

### Product Registry

Defines products and platform-owned shared-service surfaces.

Minimum fields:

- `product_id`;
- `display_name`;
- `product_type`: `product`, `platform_shared_service`, `developer_platform`;
- `owner_team`;
- `lifecycle`;
- `current_version`;
- `default_scope_prefix`;
- `status_surface_href`;
- `docs_surface_href`.

First entries:

| `product_id` | Type | Owner | Notes |
|---|---|---|---|
| `gpuaas` | product | GPUaaS/Product Platform | GPU provisioning, inventory, allocations, terminal, node lifecycle |
| `app-platform` | product | App Platform | app catalog, manifests, app instances, runtime adapters |
| `platform-shared` | platform_shared_service | Platform Architecture | IAM, billing, audit/evidence, status, notification, registry, policy |
| `app-sdk` | developer_platform | App Platform / Developer Experience | SDK examples, manifests, validation harness, docs portal track |
| `token-factory` | product | Product TBD | reserved future product entry |

### Scope Registry

Defines authorization and credential scopes used by IAM, API keys, service
accounts, product APIs, and SDK workflows.

Minimum fields:

- `scope_id`;
- `product_id`;
- `scope_kind`: `api`, `service_account`, `developer`, `operator`,
  `release`;
- `description`;
- `risk_class`;
- `allowed_actor_types`;
- `lifecycle`;
- `version`.

First entries:

| `scope_id` | Product | Kind | Risk |
|---|---|---|---|
| `gpuaas.allocations.read` | `gpuaas` | api | medium |
| `gpuaas.allocations.write` | `gpuaas` | api | high |
| `gpuaas.terminal.connect` | `gpuaas` | api | high |
| `appplatform.apps.read` | `app-platform` | api | medium |
| `appplatform.apps.publish` | `app-platform` | developer | high |
| `appplatform.runtime.operate` | `app-platform` | operator | high |
| `platform.evidence.read` | `platform-shared` | operator | medium |
| `platform.evidence.write` | `platform-shared` | release | high |
| `platform.registry.read` | `platform-shared` | operator | medium |
| `platform.registry.write` | `platform-shared` | operator | critical |

### Usage-Unit Registry

Defines billable or measurable usage units consumed by billing/rating and
product read models.

Minimum fields:

- `unit_id`;
- `owner_product_id`;
- `quantity_type`: `duration`, `count`, `storage`, `bandwidth`;
- `precision`;
- `rating_category`;
- `default_currency`;
- `lifecycle`;
- `version`.

First entries:

| `unit_id` | Owner | Quantity | Rating category |
|---|---|---|---|
| `gpu_hour` | `gpuaas` | duration | compute |
| `cpu_vm_hour` | `gpuaas` | duration | compute |
| `app_runtime_hour` | `app-platform` | duration | app_runtime |
| `token_1k` | `token-factory` | count | inference |
| `api_request` | `platform-shared` | count | platform |
| `storage_gib_month` | `platform-shared` | storage | storage |

Existing `usage_records.usage_unit` values should be validated against this
registry once Phase 1 exists.

### Audit-Action Registry

Defines action IDs for privileged and release-relevant audit/evidence rows.

Minimum fields:

- `action_id`;
- `owner_product_id`;
- `target_type`;
- `privileged`;
- `retention_class`;
- `security_review_required`;
- `evidence_type_link`;
- `lifecycle`;
- `version`.

First entries:

| `action_id` | Target | Privileged | Retention |
|---|---|---:|---|
| `platform.evidence.bundle.create` | `platform_evidence_bundle` | true | release |
| `platform.evidence.item.record` | `platform_evidence_item` | true | release |
| `platform.registry.entry.upsert` | `platform_registry_entry` | true | governance |
| `platform.iam.service_account.create` | `service_account` | true | security |
| `gpuaas.allocation.release` | `allocation` | false | product |
| `gpuaas.node.force_detach` | `node` | true | operations |
| `appplatform.artifact.publish` | `app_artifact` | true | developer |
| `appplatform.artifact.promote` | `app_artifact` | true | release |

Existing audit writers can keep writing current strings. The registry first
adds validation and discoverability; it should not rewrite historical audit
rows.

### Notification-Template Registry

Defines notification template IDs, channels, severity, and product ownership.

Minimum fields:

- `template_id`;
- `owner_product_id`;
- `channels`;
- `severity`;
- `default_enabled`;
- `required_variables`;
- `retention_class`;
- `lifecycle`;
- `version`.

First entries:

| `template_id` | Owner | Severity | Channels |
|---|---|---|---|
| `billing.low_balance_warning` | `platform-shared` | warning | email, websocket |
| `billing.balance_depleted` | `platform-shared` | critical | email, websocket |
| `provisioning.failed` | `gpuaas` | warning | websocket |
| `platform.release.blocked` | `platform-shared` | critical | email, websocket |
| `security.credential_rotated` | `platform-shared` | info | email |
| `appplatform.artifact.rejected` | `app-platform` | warning | email, websocket |

The template registry is paired with the delivery intent model in
`Notification_Policy_Portal_Surface_Model_v1.md`. Product code should emit
domain events or delivery intent, not product-local rendered notification
bodies.

### Policy-Snapshot Registry

Defines snapshot kinds and freshness budgets used by policy, entitlement, quota,
and notification-preference decisions.

Minimum fields:

- `snapshot_kind`;
- `owner_product_id`;
- `scope_kinds`;
- `freshness_budget_seconds`;
- `fail_closed_on_stale`;
- `source_family`;
- `decision_values`;
- `lifecycle`;
- `version`.

First entries:

| `snapshot_kind` | Owner | Freshness budget | Fail closed |
|---|---|---:|---:|
| `policy` | `platform-shared` | 300 | true |
| `entitlement` | `platform-shared` | 300 | true |
| `quota` | `platform-shared` | 120 | true |
| `notification_preference` | `platform-shared` | 900 | false |

`platform_policy_values` remains the implemented source for current policy keys. The
snapshot registry defines which policy-derived decisions must carry version and
freshness evidence when they affect long-lived resources or privileged writes.

### Quota-Dimension Registry

Defines cross-product quota dimensions and their usage source.

Minimum fields:

- `quota_dimension_id`;
- `owner_product_id`;
- `usage_unit_ids`;
- `scope_kinds`;
- `composition_rule`;
- `reset_window`;
- `hard_deny_allowed`;
- `lifecycle`;
- `version`.

First entries:

| `quota_dimension_id` | Owner | Usage units | Scope |
|---|---|---|---|
| `compute_allocation_slots` | `gpuaas` | `gpu_hour`, `cpu_vm_hour` | tenant, project, user |
| `app_runtime_slots` | `app-platform` | `app_runtime_hour` | tenant, project |
| `inference_tokens` | `token-factory` | `token_1k`, `api_request` | tenant, project, service_account |
| `storage_capacity` | `platform-shared` | `storage_gib_month` | tenant, project, bucket |
| `network_ingress` | `platform-shared` | `api_request` | tenant, project, route |

Quota dimensions compose through the model in
`Notification_Policy_Portal_Surface_Model_v1.md`: global defaults, product
defaults, tenant overrides, project overrides, and resource/app entitlement
overrides.

### Retention-Class Registry

Defines stable retention classes used by audit actions, notification templates,
evidence types, billing evidence, runtime logs, and later data-erasure workflows.
The registry does not execute deletion. It defines the versioned authority that
future legal-hold, archive, erasure, and runtime-log gates must evaluate.

Minimum fields:

- `retention_class_id`;
- `owner_product_id`;
- `profile_scopes`;
- `minimum_retention`;
- `hot_retention`;
- `archive_behavior`;
- `erasure_behavior`;
- `legal_hold_eligible`;
- `worm_required`;
- `data_classification`;
- `required_review_domains`;
- `source_authority`;
- `lifecycle`;
- `version`.

First entries:

| `retention_class_id` | Owner | Minimum retention | Archive / erasure posture |
|---|---|---:|---|
| `product` | `gpuaas` | 24mo | archive lifecycle evidence; erase/redact user-scoped metadata after dependency checks |
| `operations` | `platform-shared` | 24mo | archive incident/operator evidence; redact identifiers after dependency checks |
| `governance` | `platform-shared` | 7y | archive policy/review/waiver evidence; block while release/legal dependency exists |
| `security` | `platform-shared` | 7y | archive security evidence; block deletion until security review |
| `release` | `platform-shared` | 7y | archive CI/UAT/deploy/release bundles; block while production posture reconstruction is required |
| `developer` | `app-sdk` | 24mo | archive SDK/app contract evidence; erase transient developer logs after support windows |
| `audit_worm` | `platform-shared` | 7y | WORM/Object Lock required for production/regulated audit manifests |
| `runtime_logs` | `platform-shared` | 90d | archive incident-relevant bundles only; delete/redact non-incident logs after retention |
| `financial_ledger` | `platform-shared` | 10y | archive finance evidence without lossy compaction; corrections are append-only |
| `ephemeral` | `platform-shared` | TTL only | no archive; expire automatically and never use as business source of truth |

Existing `retention_class` string fields in audit, notification, and evidence
registry entries must resolve to this registry. Table-specific implementation
can remain in product/platform code, but it must use these IDs rather than
inventing local retention labels.

### Portal-Track Registry

Defines publication tracks and required review posture for portal pages.

Minimum fields:

- `portal_track_id`;
- `audience`;
- `default_visibility`;
- `required_review_domains`;
- `allowed_content_types`;
- `api_playground_mode`;
- `redaction_required`;
- `lifecycle`;
- `version`.

First entries:

| `portal_track_id` | Audience | Playground mode | Review |
|---|---|---|---|
| `internal` | product, architecture, engineering, security, ops, infra, app developers | dev/uat allowed | content owner |
| `customer` | customer admins and security reviewers | approved sandbox only | product, security, architecture |
| `partner` | partner developers and integration teams | approved sandbox only | product, developer experience, security |
| `public` | prospects and public developers | mock only | product, security |

Portal tracks are implemented first through Docusaurus page metadata and the
publication-readiness checker, not through a separate portal authorization
service.

### Evidence-Type Registry

Defines evidence item types accepted by platform evidence bundles.

Minimum fields:

- `evidence_type`;
- `owner_product_id`;
- `required_fields`;
- `retention_class`;
- `release_blocking_default`;
- `allowed_results`;
- `proves_invariant_required`;
- `lifecycle`;
- `version`.

First entries:

| `evidence_type` | Owner | Blocking default | Invariant required |
|---|---|---:|---:|
| `ci` | `platform-shared` | true | false |
| `guard_report` | `platform-shared` | true | false |
| `uat` | `platform-shared` | true | true |
| `security_scan` | `platform-shared` | true | false |
| `deployment_smoke` | `platform-shared` | true | false |
| `residual_risk` | `platform-shared` | true | false |
| `release_approval` | `platform-shared` | true | false |
| `sdk_contract` | `app-sdk` | true | true |

### Artifact-Type Registry

Defines trusted artifact kinds and promotion behavior. This registry coordinates
with existing app artifact tables and platform-control release artifact scripts;
it does not replace the OCI registry or Vault credentials.

Minimum fields:

- `artifact_type`;
- `owner_product_id`;
- `source_types`;
- `trust_states`;
- `signature_required`;
- `provenance_required`;
- `promotion_policy`;
- `runtime_eligible`;
- `lifecycle`;
- `version`.

First entries:

| `artifact_type` | Owner | Runtime eligible | Trust policy |
|---|---|---:|---|
| `oci_runtime_image` | `platform-shared` | true | digest pinned, promotion required |
| `app_bundle` | `app-platform` | true | digest pinned, verification required |
| `sdk_example` | `app-sdk` | false | source linked, contract-tested |
| `release_manifest` | `platform-shared` | false | source SHA and digest refs required |
| `security_report` | `platform-shared` | false | artifact integrity tracked |

### App SDK Contract Registry

Defines developer-facing SDK contract units. It is separate from the app catalog:
`app_catalog` lists runnable apps; this registry lists what app developers can
rely on.

Minimum fields:

- `sdk_contract_id`;
- `owner_product_id`;
- `contract_kind`: `manifest`, `launch`, `connect`, `failure`, `publish`,
  `credential`, `uat`;
- `schema_version`;
- `example_ref`;
- `validator_ref`;
- `portal_href`;
- `lifecycle`;
- `version`.

First entries:

| `sdk_contract_id` | Kind | Owner | Notes |
|---|---|---|---|
| `app.manifest.v1` | manifest | `app-sdk` | app defaults, ports, routes, auth mode, health |
| `app.launch.v1` | launch | `app-sdk` | required inputs, dependency creation, validation errors |
| `app.connect.v1` | connect | `app-sdk` | open app, try endpoint, cluster/token flows |
| `app.failure.v1` | failure | `app-sdk` | product-owned error presentation expectations |
| `app.publish.v1` | publish | `app-sdk` | artifact registration, verification, promotion |
| `app.uat.v1` | uat | `app-sdk` | launch/connect/decommission smoke expectations |

## Seed-Backed Contract Shape

Phase 0 is represented as a repo-owned YAML artifact:

```text
doc/architecture/platform-foundation/registry/platform-registry.seed.yaml
```

Validate it with:

```bash
scripts/ci/platform_registry_seed_validate.sh
```

This artifact is validated for:

- duplicate IDs inside a family;
- lifecycle values from the allowed enum;
- owner product IDs exist in the product registry;
- referenced evidence types, artifact types, and SDK contract IDs exist;
- version changes are explicit when risk class or required fields change.

## Schema-Backed Target

Phase 1 should add platform-owned tables rather than expanding product tables:

```text
platform_registry_products
platform_registry_scopes
platform_registry_usage_units
platform_registry_audit_actions
platform_registry_notification_templates
platform_registry_policy_snapshot_kinds
platform_registry_quota_dimensions
platform_registry_portal_tracks
platform_registry_evidence_types
platform_registry_artifact_types
platform_registry_sdk_contracts
platform_registry_versions
```

The shared shape can also be implemented as one normalized
`platform_registry_entries` table plus typed views. The typed-table option is
preferred if validation and discoverability are clearer.

## API Direction

Initial API should be read-only:

| Endpoint | Purpose |
|---|---|
| `GET /api/v1/v3/platform/registry/products` | list products and shared-service domains |
| `GET /api/v1/v3/platform/registry/scopes` | list IAM/API/service-account scopes |
| `GET /api/v1/v3/platform/registry/evidence-types` | list evidence item contracts |
| `GET /api/v1/v3/platform/registry/sdk-contracts` | list App SDK contract units |

Write APIs should wait until registry governance is reviewed:

- who can create product entries;
- who can promote a registry entry version;
- which changes require security/product approval;
- how existing long-lived resources snapshot registry versions.

## Consumers

| Consumer | Uses registry for |
|---|---|
| IAM facade | scope IDs, actor eligibility, service-account scope validation |
| Audit/Evidence | action IDs, evidence types, retention class, release-blocking defaults |
| Billing | usage unit validation and rating category |
| Notification | template IDs, severity, channels, preference defaults |
| App Platform | artifact types, publish/promotion trust states, app SDK contracts |
| Status/Ops | readiness gates and registry version freshness |
| Docusaurus portal | product/developer tracks, SDK contract docs, registry-derived reference tables |

## First Implementation Recommendation

1. Add seed-backed registry artifact under this directory.
2. Add a CI validator for duplicate IDs and broken owner references.
3. Add `packages/platform/registry` facade with read-only lookup interfaces.
4. Use the registry facade from the IAM facade scaffold for scope validation.
5. Add schema-backed tables only after the first consumers prove the seed shape.

Do not start with a registry admin UI or broad DB migration. The first goal is
to stop new platform work from inventing IDs and metadata in each package.

## Open Review Questions

1. Should `platform-shared` be a product registry entry or a separate
   `service_domain` family?
2. Should App SDK contract IDs live under `app-sdk` or `app-platform` ownership?
3. Should evidence types become blocking by default immediately, or only after
   the guard graduation plan is approved?
4. Which registry families need resource-version snapshots in Phase 1:
   scopes, usage units, evidence types, artifact types, or all of them?
5. Should registry write APIs require platform admin or a narrower
   governance-specific role?
